try..检索字典项的速度..除了dict中的vs.key



我经常需要检查一个键是否在字典中,如果不在,则执行其他操作。在python中,有两种明确的方法可以做到这一点:

if key in dict_:
value = dict_[key]
do_something
else:
do_something_else

try:
value = dict_[key]
except KeyError:
do_something_else
else:
do_something

以下哪个更快/更可取?这取决于字典的大小吗?

这里似乎有两个相互竞争的效果:1(必须搜索两次密钥,而不是2(设置异常堆栈跟踪。

您可以使用timeit对三种不同的方法进行基准测试。get1try...exceptget2使用内置的.getget3首先进行检查。

In [1]: def get1(d, k): 
......:     try: 
......:         return d[k] 
......:     except KeyError: 
......:         return None 
In [3]: def get2(d, k): 
......:     return d.get(k) 
In [4]: def get3(d, k): 
......:    if k in d: return d[k]  
......:    else: return None 

在一个小字典(100个元素(

In [8]: %timeit -n 100 [get1(little_d, e) for e in range(len(little_d))]                
18.8 µs ± 270 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [9]: %timeit -n 100 [get2(little_d, e) for e in range(len(little_d))]                
22.5 µs ± 352 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [10]: %timeit -n 100 [get3(little_d, e) for e in range(len(little_d))]               
19.3 µs ± 862 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)

还有一个更大的(1M元素(

In [11]: %timeit -n 100 [get1(big_d, e) for e in range(len(little_d))]
19.4 µs ± 469 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [12]: %timeit -n 100 [get2(big_d, e) for e in range(len(little_d))]
21.8 µs ± 241 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [13]: %timeit -n 100 [get3(big_d, e) for e in range(len(little_d))]
19.2 µs ± 128 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)

在"未命中"约50%的(1M(字典上(random.choices(range(0, 2*len(big_id)), k=len(big_d))(显示更多差异:

In [20]: %timeit -n 100 [get1(big_d, e) for e in choices]                              
514 ms ± 10.4 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [21]: %timeit -n 100 [get2(big_d, e) for e in choices]                              
416 ms ± 4.54 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [22]: %timeit -n 100 [get3(big_d, e) for e in choices]                              
367 ms ± 4.89 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

CCD_ 8在这种情况下仍然会更快。

In [23]: %timeit -n 100 [big_id.get(e) for e in choices]                                
334 ms ± 3.6 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

它们看起来很接近,同样受到字典大小的影响,但受到遗漏的影响不同。大小唯一真正的影响是当python开始颠簸时(就像这里可能发生的那样(。一个重要的注意事项是,get2的速度较慢,因为两个函数调用(get2.get(的开销在python中非常昂贵(如上次测试所示(。@user2864740指出,由于各种原因,未命中将导致结果变慢。

tl;dr

我会使用.get


答案也将在很大程度上取决于密钥的__hash____eq__实现的速度。如果__hash____eq__很慢,那么对hash的两个调用的差异可能表明方法1或2(没有额外的函数(更好。

最新更新