python - 为什么从元组列表创建python dict比从kwargs慢3倍
问题描述
有几种方法可以在 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 字典以哈希表的形式实现,并随着时间的推移随着键的添加而动态增长;它们从小开始,随着需要的出现,构建一个新的、更大的哈希表,数据(键、值和哈希)被复制。这在 Python 代码中是不可见的,但调整大小需要时间。但是当你使用dict(**kwargs)
(或dict(other_dict)
CPython(你用来测试的默认 Python 实现)时,可以走一条捷径:从一个足够大的哈希表开始。你不能用一个元组序列做同样的技巧,因为你可以'不知道序列中是否没有重复的键。
更多细节见该类型的 C 源码dict
,具体是dict_update_common
实现(从 调用dict_init()
);this 调用PyDict_MergeFromSeq2()
元组序列的情况,或者PyDict_Merge()
在传入关键字参数时调用。
该PyDict_MergeFromSeq2()
函数遍历序列,测试每个结果以确保有两个元素,然后实质上调用.__setitem__(key, value)
字典。这可能需要在某些时候调整字典的大小!
函数( PyDict_Merge()
via dict_merge()
) 专门检测是否传入了常规字典,然后执行一次调整内部结构大小的快速路径,然后使用调用直接从原始字典中复制散列和结构insertdict()
(按照override == 1
路径,override
如设置为1
当目标字典为空时,对于dict(**kwargs)
) 总是如此。只需调整一次大小并直接使用内部数据会快得多,需要做的工作要少得多!
所有这些都是特定于 CPython 的实现细节。其他 Python 实现,如 Jython、IronPython 和 PyPy 可以自行决定dict
类型的内部如何工作,并且会显示相同操作的不同性能差异。
推荐阅读
- javascript - 使用 ajax (JQUERY) 发送文件
- php - Wordpress - Rev Slider 不替换短代码
- javascript - safari、edge 和 IE 上的 mouseenter 和 mouseleave
- java - 服务器端访问/刷新令牌中的 Oauth2
- java - 向场景 JavaFX 添加多个组件 - 重用 fxml 模式
- python - 解析嵌套的大括号/括号组
- android - 关闭应用程序后保持服务运行
- javascript - javascript 使用 eslint 每个文件一个类
- web-services - 有人能告诉我为什么当我尝试加载 WSDL 时soapUI 返回错误吗
- three.js - 三.js使用变量设置HSL背景颜色