在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
已设置为 1
,dict(**kwargs)始终如此
)。只需调整一次大小并直接使用内部数据要快得多,需要做的工作要少得多!
所有这些都是特定于 CPython 的实现细节。其他 Python 实现(例如 Jython、IronPython 和 PyPy)可以自行决定 dict
类型的内部工作方式,并且会针对相同的操作显示不同的性能差异。
关于python - 为什么从元组列表创建 python 字典比从 kwargs 慢 3 倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52141149/