据我了解,括号只不过是一个包装器__getitem__
。以下是我对此进行基准测试的方法:
首先,我生成了一个半大字典。
items = {}
for i in range(1000000):
items[i] = 1
然后,我使用cProfile测试了以下三个功能:
def get2(items):
for k in items.iterkeys():
items.get(k)
def magic3(items):
for k in items.iterkeys():
items.__getitem__(k)
def brackets1(items):
for k in items.iterkeys():
items[k]
结果看起来像这样:
1000004 function calls in 3.779 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 3.779 3.779 <string>:1(<module>)
1 2.135 2.135 3.778 3.778 dict_get_items.py:15(get2)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1000000 1.644 0.000 1.644 0.000 {method 'get' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'iterkeys' of 'dict' objects}
1000004 function calls in 3.679 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 3.679 3.679 <string>:1(<module>)
1 2.083 2.083 3.679 3.679 dict_get_items.py:19(magic3)
1000000 1.596 0.000 1.596 0.000 {method '__getitem__' of 'dict' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'iterkeys' of 'dict' objects}
4 function calls in 0.136 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.136 0.136 <string>:1(<module>)
1 0.136 0.136 0.136 0.136 dict_get_items.py:11(brackets1)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'iterkeys' of 'dict' objects}
问题出在我进行基准测试的方式上吗?我尝试用简单的“pass”替换括号访问,以确保数据确实被访问,并发现“pass”运行得更快。我对此的解释是数据确实正在被访问。我还尝试附加到一个新列表,它给出了类似的结果。
首先是Not_a_Golfer贴出的反汇编:
>>> d = {1:2}
>>> dis.dis(lambda: d[1])
1 0 LOAD_GLOBAL 0 (d)
3 LOAD_CONST 1 (1)
6 BINARY_SUBSCR
7 RETURN_VALUE
>>> dis.dis(lambda: d.get(1))
1 0 LOAD_GLOBAL 0 (d)
3 LOAD_ATTR 1 (get)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1
12 RETURN_VALUE
>>> dis.dis(lambda: d.__getitem__(1))
1 0 LOAD_GLOBAL 0 (d)
3 LOAD_ATTR 1 (__getitem__)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1
12 RETURN_VALUE
现在,正确进行基准测试对于将任何内容解读到结果显然很重要,但我的了解还不够,无法提供太多帮助。但假设确实存在差异(这对我来说是有意义的),以下是我对为什么存在差异的猜测:
dict.get
只是“做更多”;它必须检查密钥是否存在,如果不存在,则返回其第二个参数(默认为None
)。这意味着存在某种形式的条件或异常捕获,因此我完全不感到惊讶,这与检索与键关联的值的更基本操作具有不同的时序特征。
Python 有一个用于“订阅”操作的特定字节码(如反汇编中所示)。内置类型包括dict
,主要用 C 实现,它们的实现不一定遵循正常的 Python 规则(只需要它们的接口,而且即使在那里也有很多极端情况)。所以我的猜测是,实施BINARY_SUBSCR
操作码或多或少directly到支持此操作的内置类型的底层 C 实现。对于这些类型,我希望它实际上是__getitem__
它作为 Python 级别的方法存在以包装 C 实现,而不是括号语法调用 Python 级别的方法。
基准测试可能很有趣thing.__getitem__(key)
反对thing[key]
对于实现的自定义类的实例__getitem__
;您实际上可能会看到相反的结果BINARY_SUBSCR
操作代码在内部必须回退到执行与查找方法并调用它相同的工作。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)