gpt4 book ai didi

python - 为什么 __getitem__(key) 和 get(key) 比 [key] 慢很多?

转载 作者:太空狗 更新时间:2023-10-29 17:48:42 24 4
gpt4 key购买 nike

据我了解,方括号只不过是 __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

现在,获得正确的基准测试对于将任何内容读入结果显然很重要,而我的知识还不足以帮助解决这个问题。但假设确实存在差异(这对我来说很有意义),以下是我对差异原因的猜测:

  1. dict.get 只是“做得更多”;它必须检查 key 是否存在,如果不存在则返回它的第二个参数(默认为 None)。这意味着存在某种形式的条件或异常捕获,所以我完全不惊讶这与检索与键关联的值的更基本操作具有不同的时间特征。

  2. Python 有一个用于“订阅”操作的特定字节码(如反汇编中所示)。内置类型,包括 dict,主要是在 C 中实现的,它们的实现不一定符合正常的 Python 规则(只需要它们的接口(interface),即使在那里也有很多极端情况)。所以我的猜测是,BINARY_SUBSCR 操作码的实现或多或少直接到支持此操作的内置类型的底层 C 实现。对于这些类型,我希望它实际上是作为 Python 级方法存在的 __getitem__ 来包装 C 实现,而不是括号语法调用 Python 级方法。

thing.__getitem__(key)thing[key] 进行基准测试对于实现 __getitem__ 的自定义类的实例可能很有趣>;您实际上可能会在那里看到相反的结果,因为 BINARY_SUBSCR 操作码在内部必须回退到执行与查找方法并调用它等效的工作。

关于python - 为什么 __getitem__(key) 和 get(key) 比 [key] 慢很多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10823632/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com