为什么字典的 dict.get(key) 方法比 dict[key] 运行速度慢?

17

在运行数值积分器时,我注意到根据我从字典中提取字段的方式速度有明显的差异。

import numpy as np

def bad_get(mydict):
    '''Extract the name field using get()'''
    output = mydict.get('name', None)
    return output

def good_get(mydict):
    '''Extract the name field using if-else'''
    if 'name' in mydict:
        output = mydict['name']
    else:
        output = None
    return output


name_dict = dict()
name_dict['name'] = np.zeros((5000,5000))

在我的系统上,我注意到以下差异(使用iPython)

%%timeit
bad_get(name_dict) 

The slowest run took 7.75 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 247 ns per loop

与...相比

%%timeit
good_get(name_dict)  

1000000 loops, best of 3: 188 ns per loop

这看起来似乎是一个细微的差别,但对于某些数组来说,这种差别似乎更加显著。这种行为的原因是什么,我是否应该改变对get()函数的使用方式?


很好的观察。如果你追求速度,你可以在 try/except 块中将 mydict.get("name") 替换为 mydict["name"],捕获 KeyError 并在那里分配 None - Jan Vlcinsky
1个回答

66

Python在执行dict.get()时需要做更多的工作:

  • get是一个属性,因此Python需要查找它,然后将找到的描述符与字典实例绑定。
  • ()是一个调用,因此当前帧必须被推送到栈上,进行一次调用,然后再从栈中弹出该帧以继续执行。

使用dict及其[...]表示法时,不需要单独的属性步骤或帧的推入和弹出,这一点可以通过使用Python字节码反汇编器dis来看出区别:

>>> import dis
>>> dis.dis(compile('d[key]', '', 'eval'))
  1           0 LOAD_NAME                0 (d)
              3 LOAD_NAME                1 (key)
              6 BINARY_SUBSCR
              7 RETURN_VALUE
>>> dis.dis(compile('d.get(key)', '', 'eval'))
  1           0 LOAD_NAME                0 (d)
              3 LOAD_ATTR                1 (get)
              6 LOAD_NAME                2 (key)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE

因此,d[key]表达式只需要执行一个BINARY_SUBSCR操作码,而d.get(key)会添加一个LOAD_ATTR操作码。在内置类型上,CALL_FUNCTIONBINARY_SUBSCR要昂贵得多(自定义具有__getitem__方法的类型最终仍然需要进行函数调用)。

如果您的大部分键存在于字典中,则可以使用try...except KeyError处理缺少的键:

try:
    return mydict['name']
except KeyError:
    return None

如果没有异常情况,那么异常处理是非常便宜的。


3
为什么我们不能给这样好的回答点赞并多次点赞呢?感谢您花费时间用丰富的知识解释问题。 :) - Iron Fist
2
一个额外的'try'的速度测试也将很有用,与good_get相比。 - poetyi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接