在我们疯狂之前,先看看下面的选项是否符合您的性能要求:
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库,要么进行猴子补丁。
这是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,
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)
elif isinstance(o, iterator_types):
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() ))
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)
return json.JSONEncoder.default(self, o)
li = range(10000000)
dumped = JSONEncoderThatSupportsIterators().encode(reversed(li))
assert dumped.startswith('[999999, 999998, 999997, ')
assert dumped.endswith('6, 5, 4, 3, 2, 1, 0]')
或者,你可以定义 default(self, o)
函数,并将其作为参数传递给 json.dumps(default=default)
,而不是继承 json.JSONEncoder
。
mylist.reverse()
原地反转列表(避免复制) - 进行序列化,如果需要,则再次反转它? - Jon Clements{test: range(10)}
扩展...但不适用于整个数据的reverse
。更进一步的复杂性在于,某些层次由C实现处理,而其他部分则由嵌套的_functions
处理...为了简单起见,我仍然坚持使用list.reverse
:) - Jon Clementsjson.dumps(mylist[::-1])
是另一种实现方式,但会复制列表。 - IceArdor