我一直在寻找一种以相反的顺序串行化python列表的有效方法。
我尝试json.dumps(reversed(mylist))
,但显然json.dumps
不接受迭代器。
我也可以做json.dumps(list(reversed(mylist)))
但是对于非常大的列表来说效率非常低,而且我不需要创建临时列表,我希望能够动态地序列化列表而不是创建临时列表。
我想我可以使用json.JSONEncoder
,但我真的不知道应该从default
函数返回什么。
我还必须坚持使用标准库,因为我没有自由安装其他软件包。
到目前为止,我尝试了两个提出的解决方案,这里是测试输出:
>>> 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
会更有效率,但我仍然不知道该怎么做。
答案 0 :(得分:3)
避免复制的一种方法是在原地反转列表,例如:
mylist.reverse()
json_string = json.dumps(mylist)
然后mylist.reverse()
如果需要的话,它会回来。
答案 1 :(得分:0)
在我们发疯之前,请查看以下任何一项是否符合您的性能要求:
mylist.reverse(); json.dumps(mylist); mylist.reverse()
json.dumps(mylist[::-1])
json.dumps(tuple(reversed(mylist)))
你提到定义你自己的JSONEncoder默认函数,这很简单(例如在最底层*),但我不认为它在这里工作,因为json.JSONEncoder需要默认函数将对象转换为以下之一:
None, True, False, str, int, float, list, tuple, dict
将迭代器转换为列表或元组会创建一个大对象,这是我们要避免的。
您需要修改json库或对其进行修补。
这是json.encoder的CPython源代码。 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))
替换所有出现的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
这些更改看起来像这样: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)
。