为什么dict.get(key)比dict [key]运行得慢

wil*_*il3 7 python performance dictionary

在运行数值积分器时,我注意到速度的明显差异取决于我如何在字典中提取字段的值

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))
Run Code Online (Sandbox Code Playgroud)

在我的系统上,我注意到以下区别(使用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
Run Code Online (Sandbox Code Playgroud)

相比

%%timeit
good_get(name_dict)  

1000000 loops, best of 3: 188 ns per loop
Run Code Online (Sandbox Code Playgroud)

这似乎是一个小差异,但对于某些阵列而言,差异似乎更为显着.是什么导致了这种行为,是否有某种方法我应该改变我对get()函数的使用?

Mar*_*ers 21

Python必须做更多工作dict.get():

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

[...]与a一起使用的表示法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
Run Code Online (Sandbox Code Playgroud)

所以d[key]表达式只需执行BINARY_SUBSCR操作码,同时d.get(key)添加LOAD_ATTR操作码.CALL_FUNCTIONBINARY_SUBSCR内置类型贵很多(自定义类型的__getitem__方法仍然最终进行函数调用).

如果词典中存在大多数键,则可以使用try...except KeyError处理缺失的键:

try:
    return mydict['name']
except KeyError:
    return None
Run Code Online (Sandbox Code Playgroud)

如果没有例外,异常处理很便宜.

  • 为什么我们不能再次投票和投票这样好的答案..感谢您花时间用丰富的知识解释问题.. :) (2认同)
  • 与 good_get 相比,额外“尝试”的速度测试也很有用。 (2认同)