我经常需要检查一个键是否在字典中,如果不在,则执行其他操作。在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
对三种不同的方法进行基准测试。get1
是try...except
,get2
使用内置的.get
,get3
首先进行检查。
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(没有额外的函数(更好。