检查字典中是否已存在给定键


2683

我想在更新密钥值之前测试字典中是否存在密钥。我写了以下代码:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

我认为这不是完成此任务的最佳方法。有没有更好的方法来测试字典中的键?


31
dict.keys()根据文档docs.python.org/2/library/stdtypes.html#dict.keys的说明,调用会创建一个键列表,但是如果这种模式在认真的实现中并未针对翻译进行优化,我会感到惊讶到if 'key1' in dict:
Evgeni Sergeev

7
所以,我终于找到了为什么很多我的Python脚本都是这么慢:) :(那是因为我一直在使用x in dict.keys()检查钥匙,而这发生了,因为在Java中的密钥通常的方式进行迭代的for (Type k : dict.keySet()),这种习惯导致for k in dict.keys()到感觉比自然更自然for k in dict(就性能而言应该还不错吗?),但是随后检查按键也变得if k in dict.keys()
麻烦

4
@EvgeniSergeev if k in dict_:测试dict_的KEY 中是否存在k,因此您仍然不需要dict_.keys()。(这让我很吃惊,因为它像对dict中的进行测试一样使我读起来。但事实并非如此。)
ToolmakerSteve

1
@ToolmakerSteve是的,但是您不仅不需要它,也不是一个好习惯。
Evgeni Sergeev 2013年

26
尝试“输入字典”
marcelosalloum

Answers:


3361

in是测试密钥是否存在的预期方法dict

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

如果您想使用默认值,可以随时使用dict.get()

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

如果您想始终确保任何键的默认值,则可以dict.setdefault()重复使用,也可以defaultdictcollections模块中使用,例如:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

但总的来说,in关键字是最好的方法。


74
我通常只会使用get是否要从字典中将其取出来。使用in 从字典中拉出项目没有意义。
杰森·贝克

75
我完全同意。但是,如果只需要知道某个键是否存在,或者需要区分定义键的情况和使用默认值的情况,in则是最好的方法。
克里斯B.

5
此答案的参考资料在python文档上
enkash 2015年

30
0例如,如果键等于“ False”,则get是一个错误的测试。艰难地学习了这一点:/
Sebastien

4
我不能同意这是一个完整的答案,因为它没有提到当密钥失败的数目足够小时,“ try”-“ except”将是最快的。请参阅以下答案:stackoverflow.com/a/1602945/4376643
Craig Hicks

1546

您不必呼叫按键:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

这将更快,因为它使用字典的哈希而不是进行线性搜索(调用键可以做到)。


7
这太棒了。我给人的印象是,它仍会在内部遍历键列表,但是我看到这更像测试集合中的成员资格。
Mohan Gulati

51
@Mohan Gulati:您了解字典是映射到值的键的哈希表,对吗?哈希算法将密钥转换为整数,该整数用于在哈希表中找到匹配的位置。en.wikipedia.org/wiki/Hash_table
hughdbrown

5
@Charles Addis,根据使用大约50万个键的经验,当编写“ dict中的键”而不是“ dict.keys()中的键”时,性能至少提高10倍。PEP和Zen还声明,如果它们对您的项目不利,则应忽略它们。
ivan_bilan

11
ivan_bilan-我只是在此基础上进行了自己的基准测试……在半百万个按键上if key in d1花了0.17265701293945312几秒钟。打电话来if key in d1.keys()0.23871088027954102-这是微观优化的经典定义。节省0.07884883880615234秒数并不能提高性能。
Charles Addis

11
@Eli为您创建了一个测试,您可以自己运行。结果可能会让您惊讶。对于具有约50,000个键的字典,不调用keys()将为您带来0.01秒的计算优势。对于约500,000个按键,不打电话keys()给您带来0.1秒钟的收益。对于〜5,000,000个键,不打电话的keys()速度要快0.4秒,但是对于50,000,000个键,呼叫keys()
查尔斯·亚迪斯

268

您可以使用in关键字测试字典中是否存在键:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

在更改字典之前,在字典中检查键是否存在的常见用途是对值进行默认初始化(例如,如果您的值是列表,并且您想确保可以在其后附加一个空列表)在插入键的第一个值时)。在这种情况下,您可能会发现collections.defaultdict()感兴趣的类型。

在较旧的代码中,您可能还会发现的某些用法has_key(),这是一种不赞成使用的方法,用于检查字典中键的存在(仅使用key_name in dict_name,而不是)。


2
想要分享(使用Python 2.7)我在很大程度上依赖于dict的内容的运行时间是使用“ key in dict.keys()”的363.235070,仅通过删除对“ keys( )“
Ido_f,2015年

@Ido_f,请发布您的基准,因为我的基准在3.5和2.7中几乎没有区别
Charles Addis

@Ido_f我怀疑您的程序中还有其他东西,但实际上不是key in dict.keys()。尝试除去此检查以外的所有代码,然后查看结果。
Charles Addis

101

您可以缩短此时间:

if 'key1' in dict:
    ...

但是,这充其量是对化妆品的改善。为什么您认为这不是最好的方法?


100
这是很多比化妆品更完善。使用此方法查找键的时间为O(1),而调用键将生成一个列表,且为O(n)。
杰森·贝克

5
O(1)似乎不太正确。您确定不是O(log n)吗?
频谱

12
这是单个字典查找的复杂度,平均为O(1),最坏的情况为O(n)。.list()将始终为O(n)。wiki.python.org/moin/TimeComplexity
Leonora Tindall

1
这也避免了额外的分配。(重要的是使紧圈更快一些)
nurettin

56

有关快速执行接受的答案的建议方法(10m循环)的其他信息:

  • 'key' in mydict 经过时间1.07秒
  • mydict.get('key') 经过时间1.84秒
  • mydefaultdict['key'] 经过时间1.07秒

因此,建议使用indefaultdict反对get


6
完全同意get1.84s <1.07 * 2 ;-P
Paul Rigor

54

我建议改用该setdefault方法。听起来它将满足您的所有要求。

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

9
什么是setdefault具有与OP的问题呢?
hughdbrown

18
@hughdbrown“我想在更新密钥值之前测试字典中是否存在密钥。” 有时,帖子中包含的代码会产生一连串的响应,这些响应并非最初的目标。为了实现第一句话中所述的目标,setdefault是最有效的方法,即使它不能代替所发布的示例代码。
David Berger,2009年

5
这是上乘的答案,因为它满足了OP的目标,而不仅仅是给出技术上正确的答案。请参阅:nedbatchelder.com/blog/201207/…–
Niels Bom

+1可以提供有用的答案,这使我有所收获。但是,这是否是最佳解决方案取决于编码人员的想法。例如“更新密钥值之前”的含义。如果不存在异常,他可能会抛出异常(==没有添加新密钥的权限)。也许它是一个计数字典,他将在现有计数上加1,在这种情况下,'d [key] = d.get(key,0)+ 1'是最干净的解决方案(如Chris所示,回答后被写)。(我只是懒得提这个,万一未来的读者来到这里,在考虑不同的任务。)
ToolmakerSteve

1
@ToolmakerSteve是的。这里的问题是OP的问题还不够清楚。
Niels Bom 2013年

45

python中的Dictionary具有get('key',default)方法。因此,您可以在没有密钥的情况下设置默认值。

values = {...}
myValue = values.get('Key', None)

33

如何使用EAFP(比请求更容易获得宽恕):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

查看其他SO帖子:

使用try VS如果蟒蛇

在Python中检查成员是否存在


12
如果可能通常不存在密钥,则try / except可能会更昂贵。在您引用的帖子中:“ [如果您希望99%的时间结果实际上包含可迭代的内容,我会使用try / except方法。如果异常确实是例外,它将更快。如果result为None [...] [A] n if语句总是耗费您大量的时间,几乎可以自由地设置try / except块,但是当实际发生异常时,成本要高得多。” stackoverflow.com/a/1835844/1094092
billrichards 2014年

28

使用三元运算符:

message = "blah" if 'key1' in dict else "booh"
print(message)

20

获得结果的方法是:

哪个更好取决于三个因素:

  1. 字典“通常没有钥匙”还是“通常没有钥匙”。
  2. 您是否打算使用if ... else ... elseif ... else之类的条件?
  3. 字典有多大?

了解更多:http : //paltman.com/try-except-performance-in-python-a-simple-test/

使用try / block代替“ in”或“ if”:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

2
很好,但需要针对python 3实现。我使用转换了网页的脚本2to3,并发现即使在键位于dict的情况下,不使用try语法也总是比使用try语法更快。
让·保罗

18

仅限于Python 2 :(并且python 2.7 in已经支持)

您可以使用has_key()方法:

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

22
.has_key()过时 ; 您应该使用in其他答案中所示的方法。
布拉德·科赫

12
顺便说一句,我建议您先阅读所有问题的答案,然后再回答。由于迈克尔的答案自09年以来已经存在该建议,因此该答案一无所获。(我并不是要阻止尝试添加对讨论有用的内容。请继续尝试。)
ToolmakerSteve 2013年

16

只是克里斯的补充。B(最佳答案):

d = defaultdict(int)

也可以;原因是调用int()返回0defaultdict后台操作(在构造字典时),因此在文档中称为“工厂功能”。


2
如果要创建计数字典,则应使用Counter(假设Python 2.7)。我使用defaultdict(lambda: 0)而不是defaultdict(int)因为我认为发生的事情更清楚了。0如果您int()不带参数调用,则读者不需要知道您会得到什么。YMMV。
克里斯·B

9

检查字典中是否已存在给定键

为了了解如何做到这一点,我们首先检查可以在字典上调用哪些方法。方法如下:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

检查密钥是否已存在的残酷方法可能是get()

d.get("key")

其他两种有趣的方法items()keys()听起来工作量太大。因此,让我们检查一下get()是否适合我们。我们有我们的字典d

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

打印显示我们没有的密钥将返回None

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

如果密钥存在或不存在,我们可能会用它来获取信息。但是,如果我们使用单个命令创建字典,请考虑以下问题key:None

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

get()如果某些值可能是,导致该方法不可靠None。这个故事的结局应该更快乐。如果我们使用in比较器:

print('key' in d) #True
print('key2' in d) #False

我们得到正确的结果。我们可以检查一下Python字节码:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

这表明in比较运算符不仅比更加可靠,而且甚至更快get()


.get()可以使用第二个参数default值,该参数可以用来处理where的问题key:None。例如: d.get("key", False)
Alex

.get()是最快的方法。另一个选择是分配一个try/ except
HCLivess

7

Python字典具有称为的方法__contains__。如果字典具有键,则此方法将返回True,否则返回False。

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

2
__contains__直接致电是非常糟糕的做法。正确的做法是使用in运算符,containment check即调用__contains__函数的。
user1767754

@ user1767754我正在使用foo = x['foo'] if x.__contains__('foo') else 'bar'。任何想法如何将in运算符用作此表达式的一部分?
donrondadon

1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu

5

共享使用布尔运算符检查密钥是否存在的另一种方法。

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

这返回

>>> blah
>>> blah
>>> boo
>>> boo

说明

首先,你应该知道,在Python, ,0None或长度为零的对象评估为False。其他所有内容的计算结果均为True。布尔运算从左到右求值,并且返回的操作数不是True或False。

让我们来看一个例子:

>>> 'Some string' or 1/0 
'Some string'
>>>

由于'Some string'评估为True,因此or不会评估其余的,因此不会产生除以零的误差。

但是,如果我们切换顺序,1/0则会首先评估订单并引发异常:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

我们可以使用此模式检查密钥是否存在。

(k in d and 'blah')

与...相同

if k in d:
    'blah'
else:
    False

如果键存在,这已经返回了正确的结果,但是我们希望它在不存在时打印“ boo”。因此,我们将结果or'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

1

您可以使用for循环遍历字典并获取要在字典中找到的键的名称,然后使用if条件检查其是否存在:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

检查过的代码,因为输出,这是it is existnot exist
system123456

如果要执行线性搜索,为什么还要使用字典呢?
让·弗朗索瓦·法布尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.