在运行数值积分器时,我注意到速度有明显的差异,这取决于我如何在字典中提取字段的值
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()
函数的使用?
答案 0 :(得分:15)
Python必须为dict.get()
做更多的工作:
get
是一个属性,因此Python必须查找它,然后将找到的描述符绑定到字典实例。()
是一个调用,因此必须在堆栈上推送当前帧,必须进行调用,然后必须再次从堆栈中弹出帧以继续。与[...]
一起使用的dict
表示法不需要单独的属性步骤或帧推送和弹出。
使用Python bytecode disassembler 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_FUNCTION
比内置类型的BINARY_SUBSCR
昂贵得多(带有__getitem__
方法的自定义类型最终仍会进行函数调用)。
如果您的密钥的多数存在于字典中,您可以使用try...except KeyError
来处理丢失的密钥:
try:
return mydict['name']
except KeyError:
return None
如果没有例外,异常处理便宜。