python - 为什么从元组列表创建 python 字典比从 kwargs 慢 3 倍

标签 python python-3.x performance benchmarking python-internals

在python中有几种构造字典的方法,例如:

keyvals = [('foo', 1), ('bar', 'bar'), ('baz', 100)]

dict(keyvals)

dkwargs = {'foo': 1, 'bar': 'bar', 'baz': 100}

dict(**dkwargs)

当你对这些进行基准测试时

In [0]: %timeit dict(keyvals)
667 ns ± 38 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [1]: %timeit dict(**dkwargs)
225 ns ± 7.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

您会发现第一种方法比第二种方法慢了将近 3 倍。这是为什么?

最佳答案

dict(**kwargs) 传入一个现成的字典,因此 Python 可以只复制一个已经存在的内部结构。

另一方面,元组列表需要迭代、验证、散列并将结果放入一个新的空表中。这几乎没有那么快。

Python 字典实现为 hash table ,并且随着时间的推移添加 key ,动态增长;它们从小开始,随着需要的出现,构建一个新的、更大的哈希表,复制数据(键、值和哈希)。这在 Python 代码中是不可见的,但调整大小需要时间。但是,当您使用 dict(**kwargs)(或 dict(other_dict))时,CPython(您用来测试的默认 Python 实现)可以采用快捷方式:立即从一个足够大的哈希表开始。您不能对元组序列执行相同的技巧,因为您无法预先知道序列中是否不存在重复键。 p>

更多细节参见dict类型的C源码,具体dict_update_common implementation (从 dict_init() 调用);这会为元组序列调用 PyDict_MergeFromSeq2(),或者在传入关键字参数时调用 PyDict_Merge()

PyDict_MergeFromSeq2() function遍历序列,测试每个结果以确保有两个元素,然后在字典上调用 .__setitem__(key, value)。这可能需要在某些时候调整字典的大小!

PyDict_Merge()函数(通过dict_merge())专门检测是否传入了正则字典,然后executes a fast path调整内部结构的大小一次,然后直接使用 insertdict() 调用从原始字典复制散列和结构(遵循 override == 1 路径,因为当目标字典为空时 override 已设置为 1dict(**kwargs))。只需调整一次大小并直接使用内部数据要快得多,需要做的工作要少得多!

所有这些都是特定于 CPython 的实现细节。其他 Python 实现(例如 Jython、IronPython 和 PyPy)可以自行决定 dict 类型的内部工作方式,并且会针对相同的操作显示不同的性能差异。

关于python - 为什么从元组列表创建 python 字典比从 kwargs 慢 3 倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52141149/

相关文章:

python - 组织一个Python应用程序

python - 制作 "durable"对象

python - statsmodels OLS 在 python 中给出 TypeError

python - 在 Python 3.7 中更改 NIFTI 的整个图像切片

Python: string.replace ("\' ","' ") 读取txt文件后

javascript - 为什么改变对象的 [[prototype]] 会降低性能?

python - 如何在pkg_resources中实现resource_filename?

python - 根据转换是否可以增量运行来更改输入

sql-server - SSIS:一个数据流中的多个数据流还是......?

C++ 转换优化