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


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

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

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


2189
2017-10-21 19:05


起源


调用 dict.keys() 根据文档,创建密钥列表 docs.python.org/2/library/stdtypes.html#dict.keys 但是如果这个模式没有针对一个严肃的实现进行优化,我会感到惊讶 if 'key1' in dict:。 - Evgeni Sergeev
所以我终于找到了为什么我的许多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() 这也是一个问题...... - Evgeni Sergeev
@EvgeniSergeev if k in dict_: 测试dict_的KEYS中是否存在k,所以你仍然不需要 dict_.keys()。 (这有点让我感到厌烦,因为它对我来说就像它的测试一样 值 在dict。但事实并非如此。) - ToolmakerSteve
@ToolmakerSteve这是对的,但你不仅不需要它,这不是一个好习惯。 - Evgeni Sergeev
尝试“键入dict” - marcelosalloum


答案:


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

d = dict()

for i in xrange(100):
    key = i % 10
    if key in d:
        d[key] += 1
    else:
        d[key] = 1

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

d = dict()

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

...并且如果您想要始终确保您可以使用的任何键的默认值 defaultdict 来自 collections 模块,像这样:

from collections import defaultdict

d = defaultdict(lambda: 0)

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

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


2242
2017-10-21 19:10



我通常只是用 get 如果我打算把这个项目拉出字典。使用没有意义 in  和 将项目拉出字典。 - Jason Baker
我完全同意。但是,如果您只需要知道密钥是否存在,或者您需要区分定义密钥的情况和使用默认密钥的情况, in 这是最好的方式。 - Chris B.
参考 这个答案是在python文档 - enkash
如果密钥等于“False”,则get是一个糟糕的测试 0 例如。学到了很多:/ - Sebastien
我不能同意这是一个完整的答案,因为它没有提到'尝试' - '除了'将是最快的,当密钥失败的数量足够小时。请参阅以下答案: stackoverflow.com/a/1602945/4376643 - Craig Hicks


您不必调用密钥:

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

那会很多 更快 因为它使用字典的散列而不是进行线性搜索,调用键可以做。


1099
2017-10-21 19:06



太棒了。我的印象是它内部仍会遍历键列表,但我认为这更像是测试集合中的成员资格。 - Mohan Gulati
@Mohan Gulati:你明白字典是映射到值的键的哈希表,对吧?散列算法将密钥转换为整数,整数用于在散列表中查找匹配的位置。 en.wikipedia.org/wiki/Hash_table - hughdbrown
@Charles Addis,根据使用大约五十万个键的经验,在写入“dict in key”而不是“key in dict.keys()”时,你获得至少10倍的性能提升。 PEP和Zen还声明,如果它们对您的项目不利,您应该忽略它们。 - ivan_bilan
ivan_bilan - 我刚刚在这上面进行了自己的测试......在五十万个按键上, if key in d1 拿 0.17265701293945312 秒。调用 if key in d1.keys() 拿 0.23871088027954102  - 这是微优化的经典定义。保存 0.07884883880615234 秒不是性能提升。 - Charles Addis
@Eli为了你我已经创建了一个你可以自己运行的测试。结果可能让你大吃一惊。对于带有~50,000个键的dicts,而不是调用 keys() 给你.01秒计算好处。对于~500,000个按键,不要拨打电话 keys() 给你0.1秒的好处。对于约5,000,000个钥匙,不要打电话 keys() 是快.4秒,但50,000,000键 CALLING keys() 快3秒了! - Charles Addis


您可以使用。来测试字典中是否存在密钥  关键词:

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,而是)。


226
2017-10-21 19:16



dict.has_key(key)已被弃用,支持dict中的key - David Locke
从技术上讲, has_key 是 弃用 对于Python 2.x +(不仅仅是3.0+)。也就是说,即使在Python 2.x中编写,也建议不要使用新代码。 (因为它是一个已知在未来版本中会消失的功能,而且有一个非常好的替代品可以替代使用。)3.0中发生的是它完全被删除。 - ToolmakerSteve
@ToolmakerSteve你当然是正确的,我更新了答案以反映这一点。 :) - kqr
想要分享(使用Python 2.7)我刚写的东西的运行时间,严重依赖于dicts,是使用“key in dict.keys()”的363.235070并且通过删除“密钥”的调用大幅下降到0.260186( )” - Ido_f
@Ido_f请发布您的基准测试,因为我的基准测试几乎没有3.5和2.7的差异 - Charles Addis


你可以缩短这个:

if 'key1' in dict:
    ...

然而,这充其量只是一种美容改善。为什么你认为这不是最好的方法?


74
2017-10-21 19:06



这是 许多 不仅仅是美容改善。使用此方法查找密钥的时间是O(1),而调用密钥将生成一个列表并且为O(n)。 - Jason Baker
O(1)似乎不太正确。你确定它不像O(log n)吗? - spectras
这是单个dict查找的复杂性,平均为O(1),最差为O(n)。 .list()将始终为O(n)。 wiki.python.org/moin/TimeComplexity - Leo Tindall


我建议使用 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}

40
2017-10-21 19:07



是什么 setdefault 与OP的问题有关系吗? - hughdbrown
@hughdbrown“我想在更新密钥的值之前测试密钥中是否存在密钥。”有时帖子包含的代码可以产生一些不完全是最初目标的响应。为了实现第一句中所述的目标,setdefault是最有效的方法,即使它不是发布的示例代码的替代品。 - David Berger
这是最好的答案,因为它满足了OP的目标,而不仅仅是提供技术上正确的答案。看到: nedbatchelder.com/blog/201207/... - Niels Bom
+1给了一个信息性答案,它教会了我一些东西。然而,它是否是最佳解决方案取决于编码器的想法;例如“更新密钥值之前”的含义。如果它不存在,也许他会抛出异常(==没有添加新密钥的权限)。也许它是一个计数字典,他将在现有计数中加1,在这种情况下,`d [key] = d.get(key,0)+ 1'是最干净的解决方案(正如克里斯在你的回答中所示)写的)。 (我只是提到这一点,以防未来的读者来到这里,考虑到不同的任务。) - ToolmakerSteve
@NielsBom ...恕我直言setdefault是 只要 该 优越 现有条目的解决方案 不 被覆盖。 (一个重要的案例,但不是测试密钥存在的唯一原因。) - ToolmakerSteve


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

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

因此使用 in 要么 defaultdict 建议反对 get


35
2018-05-29 11:06



get本质上是子弹点1和3的组合。 - scape
完全同意 get1.84s <1.07 * 2 ;-P - Paul Rigor


python中的字典有一个get('key',default)方法。所以你可以设置一个默认值,以防没有密钥。

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

19
2018-03-01 09:03





如需检查,您可以使用 has_key() 方法

if dict.has_key('key1'):
   print "it is there"

如果你想要一个值,那么你可以使用 get() 方法

a = dict.get('key1', expeced_type)

如果要将元组或列表或字典或任何字符串作为默认值作为返回值,请使用 get() 方法

a = dict.get('key1', {}).get('key2', [])

15
2017-09-10 18:37



.get和has_key已经在你的答案中提出了几年,has_key也已在python3中删除了 - Padraic Cunningham


使用三元运算符:

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

14
2017-08-18 22:58