dict的get方法引起的性能问题
2010 12 15 12:39 AM 2244次查看
很容易想到3种方法来解决这个问题:先判断key是否存在,然后再获取;直接获取,但是捕捉异常;使用get方法的第二个参数。
测试代码如下:
from time import clock
a = dict(zip(xrange(100),xrange(100)))
c = clock()
for i in xrange(1000000):
b = a[1] if 1 in a else None
print clock() - c
c = clock()
for i in xrange(1000000):
try:
b = a[1]
except:
b = None
print clock() - c
c = clock()
for i in xrange(1000000):
b = a.get(1, None)
print clock() - c
c = clock()
for i in xrange(1000000):
if 100 in a:
b = a[100]
else:
b = None
print clock() - c
c = clock()
for i in xrange(1000000):
b = a[100] if 100 in a else None
print clock() - c
c = clock()
for i in xrange(1000000):
b = a.get(100, None)
print clock() - c
结果:0.194694297739看上去用in判断是最高效的;而如果大部分情况下都不会出错的话,使用try似乎更佳;但很明显大家都会选择第三种方法,因为书写起来最简单。
0.145797301054
0.255794242006
0.135311331468
1.11606084013
0.258792947148
当然,我要说的性能问题并不是指这个,毕竟1百万次相差还不到0.1秒。问题是get方法有个陷阱可能会诱惑一些人。
举例来说,我有5个字典,我依次尝试从它们之中取一个值,然后返回最先找到的值或None。那么我很可能会这样写:
dict1.get(key, dict2.get(key, dict3.get(key, dict4.get(key, dict5.get(key, None)))))
它看上去并不难懂,而且肯定能得到正确的结果,但是却存在一个性能问题。原因就是它虽然看上去像是短路求值,实际上却完全不是这么回事。要调用get方法,Python在执行时必须计算它的所有参数,然后再压栈调用。
因此这段代码在执行时首先会计算dict5.get(key, None),然后依次是dict4、dict3、dict2和dict1的get()。
可以看到,无论key是否存在,这5个字典都必须遍历一次,造成了很大的浪费。
那么下面这种写是否正确呢?
dict1.get(key, None) or dict2.get(key, None) or dict3.get(key, None) or dict4.get(key, None) or dict5.get(key, None)
答案仍然是不正确,因为有可能出现值存在,但真值为False的情况,这样逻辑就完全错误了。由此可见,dict的get方法最好不要用于短路求值中,除非你确信这样做没问题。
向下滚动可载入更多评论,或者点这里禁止自动加载。