在Python中检查字典中键的正确方法
我一直在想,假设有一个字典d……那么检查一个键是否在这个字典里,最正确(更符合Python风格)的方法是什么呢:
k in d
or
k in d.keys()
还是说有其他的方法呢??
5 个回答
在字典 d 中使用 k 是比较高效的。当你使用 d.keys() 时,其实是把所有的键提取出来,放到一个列表里,然后再去这个列表里查找。所以它需要先生成一个列表,列表的大小会影响性能,越大速度就越慢。虽然有更好的方法来检查耗时,但这个例子可以帮助你理解它们之间的区别:
d={}
for i in range(1000000):
d[i]=i*2
start_time = time.time()
if 2 in d:
print "yes"
print("-key in d -- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
if 2 in d.keys():
print "yes"
print("--key in d.keys()- %s seconds ---" % (time.time() - start_time))
我电脑上的输出结果:
yes
-key in d -- 0.0 seconds ---
yes
--key in d.keys()- 0.0169999599457 seconds ---
我刚在Python 3中试了一下,出于好奇,确实这两者现在看起来都是O(1)的操作,这和Python 2相比有很大不同。
import random
import string
def rand_string(length):
""" Generates a random string of numbers, lower- and uppercase chars. """
return ''.join(random.choice(
string.ascii_lowercase + string.ascii_uppercase + string.digits)
for i in range(length)
)
big_dict = {rand_string(20):rand_string(20) for i in range(10000)}
%timeit 10 in big_dict
%timeit 'M99FvcvRTcnl782Hlv2S' in big_dict
10000000 loops, best of 3: 62.2 ns per loop
10000000 loops, best of 3: 60.4 ns per loop
%timeit 10 in big_dict.keys()
%timeit 'M99FvcvRTcnl782Hlv2S' in big_dict.keys()
10000000 loops, best of 3: 140 ns per loop
10000000 loops, best of 3: 140 ns per loop
比较
为了记录一下,这里有一个比较(感谢 timeit
模块),也包含了 try/except 的情况。
代码
# Comparison of checking if key is in a dict
lend = 200
idx = 200-1
d = {}
for i in range(lend):
d[i]=i*2
def ktry():
try:
d[idx]
# return True if d[idx] else True
# return True
except KeyError:
return False
else:
return True
def kind():
return idx in d
def kindkeys():
return idx in d.keys()
if __name__ == '__main__':
import timeit
print(timeit.timeit("kind()", setup="from __main__ import kind"))
print(timeit.timeit("ktry()", setup="from __main__ import ktry"))
print(timeit.timeit("kindkeys()", setup="from __main__ import kindkeys"))
结果
以下是不同字典长度和键是否在字典中的一些时间结果(单位:秒):
短字典:
lend = 200
idx = 0
>> 0.18031001091 # k in d
>> 0.216886997223 # try/except
>> 1.06729197502 # k in d.keys()
不在短字典中的情况:
lend = 200
idx = 201
>> 0.178912878036 # k in d
>> 1.32136297226 # try/except
>> 4.93310189247 # k in d.keys()
长字典:
lend = 20000
idx = 1
>> 0.178980827332 # k in d
>> 0.22277712822 # try/except
>> 105.207716942 # k in d.keys()
不在长字典中的情况:
lend = 20000
idx = 20001
>> 0.184767007828 # k in d
>> 1.38200902939 # try/except
>> 490.606647968 # k in d.keys()
结论
当键在字典中时,使用 try/except 的方法比 k in d
稍慢一些。不过,如果键不在字典中,try/except 的表现就比较差,但比 k in d.keys()
要好。
综合考虑性能和可读性,还是应该优先使用 k in d
。
使用 k in d
是最好的方法,因为它会更快,时间复杂度是 O(1)。这是因为它利用了字典的哈希功能,而不是像线性搜索那样逐个查找,这样会慢很多。
还有一个替代方法是使用 dict.has_key(key)
,不过要注意的是,"has_key" 在 Python 3.0 及以上版本中已经不推荐使用了。
总是使用
k in d
这是一种 O(1) 的常数时间操作。
而另外一种方法,k in d.keys()
,在 Python 2 中是 O(N) 的线性时间操作,最多也只是 Python 3 中一个不必要的方法调用。不要使用它。
d.keys()
需要多做 3 步:查找属性、调用函数,还要创建一个新对象。在 Python 2 中,这个新对象是一个包含所有键的列表(这需要先列出这些键),而在 Python 3 中,它是一个字典视图。对于仅仅是检查某个键是否存在来说,这两种方法都是 完全多余的。而在 Python 2 中,检查这个列表对象是否包含某个键时,需要扫描所有元素,直到找到匹配项。