在Python中检查字典中键的正确方法

2 投票
5 回答
2069 浏览
提问于 2025-04-19 21:25

我一直在想,假设有一个字典d……那么检查一个键是否在这个字典里,最正确(更符合Python风格)的方法是什么呢:

k in d
  or
k in d.keys()

还是说有其他的方法呢??

5 个回答

1

在字典 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 ---
1

我刚在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
2

比较

为了记录一下,这里有一个比较(感谢 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

3

使用 k in d 是最好的方法,因为它会更快,时间复杂度是 O(1)。这是因为它利用了字典的哈希功能,而不是像线性搜索那样逐个查找,这样会慢很多。

还有一个替代方法是使用 dict.has_key(key),不过要注意的是,"has_key" 在 Python 3.0 及以上版本中已经不推荐使用了。

20

总是使用

k in d

这是一种 O(1) 的常数时间操作。

而另外一种方法,k in d.keys(),在 Python 2 中是 O(N) 的线性时间操作,最多也只是 Python 3 中一个不必要的方法调用。不要使用它。

d.keys() 需要多做 3 步:查找属性、调用函数,还要创建一个新对象。在 Python 2 中,这个新对象是一个包含所有键的列表(这需要先列出这些键),而在 Python 3 中,它是一个字典视图。对于仅仅是检查某个键是否存在来说,这两种方法都是 完全多余的。而在 Python 2 中,检查这个列表对象是否包含某个键时,需要扫描所有元素,直到找到匹配项。

撰写回答