2017-08-02 1 views
2

私は、逆の順序でPythonリストを直列化する効率的な方法を探していました。Pythonの効率的なリバースリストJSONのシリアル化

私はjson.dumps(reversed(mylist))をしようとしましたが、明らかにjson.dumpsはイテレータを受け入れません。

私はjson.dumps(list(reversed(mylist)))でも非常に効率的ではありませんが、一時的なリストを作成する必要はありません。私は一時的なリストを作成する代わりに、その場でリストを連載したいと考えていました。

私はjson.JSONEncoderを使用できると思いますが、defaultの機能から戻ってくるべきことはありません。

私は他のパッケージをインストールする自由がないので、私は標準ライブラリに固執する必要があります。

は、これまでのところ私は2つの提案されたソリューションを試してみましたが、ここでのテスト出力です:

>>> timeit.timeit('li.reverse(); json.dumps(li)', number=1, globals=globals()) 
2.5034537549945526 
>>> timeit.timeit('"[{}]".format(",".join(map(json.dumps,reversed(li))))', number=1, globals=globals()) 
41.076039729989134 

私はまだ自分のJSONEncoderを実装する方が効率的であろうと考えていますが、私はまだ正確に行うには方法がわかりませんそれ。バック

mylist.reverse() 
json_string = json.dumps(mylist) 

その後mylist.reverse()それはニーズがあることとします。

+0

'mylist.reverse()'を使ってリストを逆順にします(コピーを避けます)。 - あなたのシリアライゼーションを行います。 –

+0

新しいリストを作成するよりも優れていますが、それでもまだ必要のない中間ステップが作成されます。しかし、ヒントのおかげで。 :) – silentnights

+1

jsonライブラリを見ていて、それは見た目ほど単純ではありません。 JSONDecoder.defaultには、*例えば、任意のイテレータをサポートするために、* ...でも可能ですが、それは、その部分集合に意味のあるiterableからリストを返すことを示唆しています(例えば、 '{test:range (10)} '展開されましたが、あなたのデータの' reverse '全体ではありません。いくつかのレベルは、C実装やその他のビットで '_functions'を入れ子にした' _functions'で処理するという点でさらに複雑です。私は 'list.reverse'をしっかりとしています:) –

答えて

3

コピーを回避するための1つの方法は、例えば、リストのインプレースを逆にすることです。

0

私たちは夢中になる前に、次のいずれかがあなたのパフォーマンス要件を満たすかどうかを確認:

mylist.reverse(); json.dumps(mylist); mylist.reverse() 
json.dumps(mylist[::-1]) 
json.dumps(tuple(reversed(mylist))) 

あなたは(*一番下に例)を行うことは非常に簡単です、あなた自身のJSONEncoderのデフォルトの関数を定義言及

None, True, False, str, int, float, list, tuple, dict 

ラージオブジェクトを作成し、リストやタプルにイテレータを変換する、:json.JSONEncoderは、次のいずれかにオブジェクトを変換するには、デフォルトの機能を必要とするので、私はそれがここで働くとは思いませんこれは私たちが避けようとしているものです。

jsonライブラリを変更するか、monkey-patchする必要があります。

ここにCPythonのソースコードjson.encoderがあります。 PyPy、Jython、その他のPythonの実装はおそらくjsonモジュールに同じコードを使用しています。パフォーマンス上の理由から

https://github.com/python/cpython/blob/master/Lib/json/encoder.py#L204

def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, 
    _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, 
    ## HACK: hand-optimized bytecode; turn globals into locals 
    ValueError=ValueError, 
    dict=dict, 
    float=float, 
    id=id, 
    int=int, 
    isinstance=isinstance, 
    list=list, 
    str=str, 
    tuple=tuple, 
    _intstr=int.__str__, 
    ... 
    def _iterencode(o, _current_indent_level): 
     if isinstance(o, str): 
      yield _encoder(o) 
     ... 
     elif isinstance(o, (list, tuple)): 
      yield from _iterencode_list(o, _current_indent_level) 

     # Add support for processing iterators 
     elif isinstance(o, iterator_types): 
      # Side-effect: this will consume the iterator. 
      # This is probably why it's not included in the official json module 
      # We could use itertools.tee to be able to iterate over 
      # the original iterator while still having an unconsumed iterator 
      # but this would require updating all references to the original 
      # iterator with the new unconsumed iterator. 
      # The side effect may be unavoidable. 
      yield from _iterencode_list(o, _current_index_level) 

は、関数の外にイテレータ型を定義し、ローカルとしてそれを持って帰りたいと思うでしょう。

str_iterator = type(iter(str() )) 
list_iterator = type(iter(list() )) 
tuple_iterator = type(iter(tuple() )) 
range_iterator = type(iter(range(0))) 
list_reverseiterator = type(reversed(list() )) 
reverseiterator  = type(reversed(tuple())) #same as <class 'reversed'> 

# Add any other iterator classes that you need here, plus any container data types that json doesn't support (sets, frozensets, bytes, bytearray, array.array, numpy.array) 
iterator_types = (str_iterator, list_iterator, tuple_iterator, range_iterator, 
        list_reverseiterator, reversed) 

猿パッチルートに移動する場合は、json.encoderを再定義する必要があります。_make_iterencode機能、これらの変更は、このような何かを見isinstance(X, (list, tuple)+iterator_types)

import json 
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, 
     _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, 
     iterable_types=_get_iterable_types(), 
     ... 
    ): 
    ... 

json.encoder._make_iterencode = _make_iterencode 

isinstance(X, (list, tuple))の出現をすべて置き換える:約束どおりコピーせずにイテレータをダンプするために有用ではないものの、独自のデフォルトの関数を定義する方法、https://github.com/python/cpython/pull/3034/files

*をイテレータをリストまたはタプルに挿入します。

また
class JSONEncoderThatSupportsIterators(json.JSONEncoder): 
    def default(self, o): 
     try: 
      iterable = iter(o) 
     except TypeError: 
      pass 
     else: 
      return list(iterable) 
     # Let the base class default method raise the TypeError 
     return json.JSONEncoder.default(self, o) 

li = range(10000000) # or xrange if Python 2 
dumped = JSONEncoderThatSupportsIterators().encode(reversed(li)) 
assert dumped.startswith('[999999, 999998, 999997, ') 
assert dumped.endswith('6, 5, 4, 3, 2, 1, 0]') 

、というよりもjson.JSONEncoderをサブクラス化するには、default(self, o)関数を定義し、json.dumps(default=default)への引数として渡すことができます。

関連する問題