Python中大嵌套字典的最快复制方法及C扩展问询
快速复制大型嵌套字典:为什么ujson的序列化/反序列化更快?
你的测试结果完全合理,而且这种“序列化再反序列化”比纯Python复制更快的现象,核心原因在于纯Python代码与C扩展的执行效率鸿沟。下面我会拆解背后的逻辑,再给你一些其他可选的高效方案。
为什么ujson.loads(ujson.dumps(my_dict))能跑这么快?
我们逐个对比你测试的三种方法:
deepcopy:它是纯Python实现的通用复制工具,需要处理所有Python对象类型(不仅是字典,还有列表、自定义类、循环引用等),全程依赖Python解释器的字节码执行,递归遍历、类型检查、内存操作的开销都很大,处理大字典时这些开销会被无限放大。- 自定义
fast_copy:虽然你针对性只处理字典嵌套,减少了不必要的类型判断,但它依然是纯Python递归函数——每一次函数调用、循环迭代、条件判断都要经过Python解释器的层层处理,速度还是赶不上C层面的操作。 ujson方案:ujson是完全用C实现的JSON序列化库,dumps和loads全程在C层面执行:dumps把Python字典转成JSON字符串时,C代码直接操作内存,遍历字典的效率比Python循环高几个数量级;loads把JSON字符串转回字典时,同样是C代码直接构建新的Python字典对象,没有Python解释器的额外开销。
中间的字符串转换开销,对于大字典来说完全被C层面的高速操作抵消,甚至比纯Python复制更快。
有没有其他更快的C扩展方案?
除了ujson,还有几个专门针对Python数据结构的高效C扩展可以尝试:
pickle的C实现:Python3的pickle默认使用C实现(即_pickle),支持更多Python对象类型,但序列化后的体积比JSON大,速度通常略逊于ujson,但依然比deepcopy快。测试代码:import pickle %timeit pickle.loads(pickle.dumps(x))msgpack:二进制序列化库,比JSON更紧凑,序列化/反序列化速度也很出色,同样是C扩展实现:import msgpack %timeit msgpack.loads(msgpack.dumps(x))pyarrow序列化:面向大数据场景的库,对嵌套结构的序列化有优化,还支持跨语言:import pyarrow as pa %timeit pa.deserialize(pa.serialize(x).to_buffer())
不过对于你的嵌套字典场景,ujson已经是非常高效的选择——它的序列化逻辑完全匹配字典的键值对结构,额外开销极小。
你的测试代码参考
先贴出你的测试代码,方便对照:
x = {'a':1,'b':2,'c':3,'d':4, 'e':{'a':1,'b':2}} # 自定义递归复制函数(仅处理字典嵌套) def fast_copy(d): output = d.copy() for key, value in output.items(): output[key] = fast_copy(value) if isinstance(value, dict) else value return output from copy import deepcopy import ujson %timeit deepcopy(x) # 13.5 µs ± 146 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) %timeit fast_copy(x) # 2.57 µs ± 31.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) %timeit ujson.loads(ujson.dumps(x)) # 1.67 µs ± 14.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
从结果能明显看到,C扩展实现的ujson方案比纯Python方法快得多,而且字典越大,这种效率差距会越明显——毕竟Python循环的开销是线性放大的,而C的循环开销几乎可以忽略。
总结
如果你的需求只是快速复制符合JSON规范的嵌套字典,ujson.loads(ujson.dumps(my_dict))确实是最优方案之一,核心优势就是C扩展的执行效率远高于纯Python代码。如果需要支持更多Python对象类型,可以尝试msgpack或pickle的C实现,但字典场景下ujson的表现通常是最好的。
内容的提问来源于stack exchange,提问作者Idok
相关产品推荐
相关产品推荐

