如何在一次通过中检查多个键是否在字典中?


217

我想做类似的事情:

foo = {'foo':1,'zip':2,'zam':3,'bar':4}

if ("foo","bar") in foo:
    #do stuff

如何检查dict foo中是否同时包含“ foo”和“ bar”?

Answers:


362

好吧,你可以这样做:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!

10
+1,我更喜欢Greg的答案,因为它更简洁,更快捷(无需建立无关的临时列表,并且可以充分利用短路)。
亚历克斯·马丁里

4
我爱all()和any()。它们使许多算法变得更加干净。
hughdbrown

我最终使用了该解决方案。对于大型数据集,这似乎是最好的。当检查说25或30键。

4
由于短路,这是一个很好的解决方案,尤其是如果测试失败的频率很高。除非您可以只创建一次感兴趣的密钥集并进行多次检查,否则这种情况set更好。像往常一样...测量它!-)
亚历克斯·马丁里

每当它看起来比“常规”方式好,并带有所有“与”或“或” ...时,我都会使用它。这也很好,因为您可以使用“全部”或“任何” ...此外,您可以使用“ k in foo”或“ k not in foo”,具体取决于您要执行的测试
Terence Honles

123
if {"foo", "bar"} <= myDict.keys(): ...

如果您仍在使用Python 2,则可以执行

if {"foo", "bar"} <= myDict.viewkeys(): ...

如果您仍然使用的旧版本<= 2.6的Python,则可以调用setdict,但是它将遍历整个dict以构建集合,这很慢:

if set(("foo", "bar")) <= set(myDict): ...

看起来挺好的!我唯一不喜欢的是您必须创建临时集,但是它非常紧凑。因此,我必须说...套子很好用!
特伦斯·洪尔斯09年

17
在python 3中,您可以说set(("foo","bar")) <= myDict.keys()避免了临时设置,因此速度更快。对于我的测试,当查询为10个项目时,其速度与使用全部速度差不多。但是随着查询变大,它变得越来越慢。
约翰·拉鲁伊

1
我已经发布了一些测试作为答案。stackoverflow.com/questions/1285911/…–
约翰·拉鲁伊

30
if {'foo', 'bar'} <= set(myDict): ...
鲍里斯·赖希夫

11
对于任何想知道为什么这样做的人:运算符<=与使用.set issubset()方法相同:docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
edepe

41

3个替代方案的简单基准测试平台。

输入您自己的D和Q值


>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''

>>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632499
0.28672504425048828

#This one only works for Python3
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632084
2.5987625122070312e-05

>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632219
1.1920928955078125e-05

4
Python 2.7版已经d.viewkeys()做出set(q) <= d.viewkeys()
马丁·彼得

Python 2.7.5也有d.keys()方法。
伊万·哈拉莫夫

3
@IvanKharlamov,但是在Python2中,它不会返回与set(q) <= ...
John La Rooy

1
不好意思,您绝对会发现:它返回TypeError: can only compare to a set。抱歉! :))
Ivan Kharlamov

1
对于Python 2,请切换顺序:d.viewkeys() >= set(q)。我来这里是为了找出订单为什么重要!
Veedrac

34

您不必将左侧包裹在一组中。您可以这样做:

if {'foo', 'bar'} <= set(some_dict):
    pass

这也比all(k in d...)解决方案要好。


2
这也比all(k in d ...)解决方案的性能更好。我建议将此作为编辑,但由于理由而被拒绝,最好添加评论。所以这就是我要做的
miraculixx 2015年

@miraculixx添加评论不是更好。最好将相关信息编辑为答案并删除评论。
endlith 2013年

1
@endolith我同意,有些人显然不这样做,正如您在我被拒绝的编辑中看到的那样。无论如何,这不是针对meta的讨论。
miraculixx

有人可以解释一下吗?我已经收集到{}创建了一个集合,但是小于等于运算符在这里如何工作?
Locane

1
@Locane <=运算符测试第一组是否是第二组的子集。您也可以执行{'foo','bar'}。issubset(somedict)。集合方法论的文档可以在这里找到: docs.python.org/2/library/sets.html
喵喵,

24

使用

if set(("foo", "bar")).issubset(foo):
    #do stuff

或者:

if set(("foo", "bar")) <= set(foo):
    #do stuff

2
我在回答中使用的set(d)就像set(d.keys())一样,但速度更快,更短,从风格上讲我更喜欢。
亚历克斯·马丁里

set(d)set(d.keys())(没有d.keys()构造中间列表的)相同
Jochen Ritzel 09年

11

这个怎么样:

if all([key in foo for key in ["foo","bar"]]):
    # do stuff
    pass

8
实际上,不仅不必要,而且对人体有害,因为它们会阻碍的正常短路行为all
Alex Martelli


9

虽然我喜欢Alex Martelli的答案,但对我来说似乎不是Pythonic。也就是说,我认为成为Pythonic的重要部分是易于理解。有了这个目标,<=并不容易理解。

虽然字符更多,但issubset()按照卡尔·福格特兰(Karl Voigtland)的答案所建议的用法更容易理解。由于该方法可以将字典用作参数,因此一个简短的,可理解的解决方案是:

foo = {'foo': 1, 'zip': 2, 'zam': 3, 'bar': 4}

if set(('foo', 'bar')).issubset(foo):
    #do stuff

我想用{'foo', 'bar'}代替set(('foo', 'bar')),因为它更短。但是,这不是很容易理解,我认为花括号像字典一样容易被混淆。


2
我认为这是可以理解的。
Bobort

这是在文档中作为一个代名词.issubset()。我认为加入Python文档会使其默认为Pythonic。
ingyhere

4

Alex Martelli的解决方案set(queries) <= set(my_dict)是最短的代码,但可能不是最快的。假设Q = len(查询)和D = len(my_dict)。

这需要O(Q)+ O(D)来创建两个集合,然后(一个希望!)仅O(min(Q,D))进行子集测试-当然,假设Python进行了查找是O(1)-这是最坏的情况(当答案为True时)。

休格布朗(et al?)的生成器解all(k in my_dict for k in queries)为最坏情况O(Q)。

复杂因素:
(1)基于集合的小工具中的循环全部以C速度完成,而基于Any的小工具则在字节码上循环。
(2)基于任何内容的小工具的调用者都可以使用任何失败概率的知识来对查询项目进行相应的排序,而基于集合的小工具则不允许这样的控制。

与往常一样,如果速度很重要,则在操作条件下进行基准测试是一个好主意。


1
对于我尝试过的所有情况,生成器都更快。stackoverflow.com/questions/1285911/…–
约翰·拉鲁伊

2

您可以使用.issubset()以及

>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>

1

使用lambda怎么样?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff

2
这个答案是唯一在功能上正确的答案,它将通过简单的更改(s / True / 1 /)在Python 1.5上运行……但是它没有其他用途。而且True最好将其作为可选的初始化程序arg而不是挤在序列arg的前面。
约翰·马钦

1

如果您想:

  • 还获取键的值
  • 检查一个以上的字典

然后:

from operator import itemgetter
foo = {'foo':1,'zip':2,'zam':3,'bar':4}
keys = ("foo","bar") 
getter = itemgetter(*keys) # returns all values
try:
    values = getter(foo)
except KeyError:
    # not both keys exist
    pass

1

并不是说这不是您没有想到的事情,但是我发现最简单的事情通常是最好的:

if ("foo" in foo) and ("bar" in foo):
    # do stuff

1
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason()在Python中不是必需的。


3
仍然它们可能是很好的样式...没有它们,我的C ++大脑总是想知道它是否将被解释为“如果foo中的'foo in(foo
and'bar

1
我了解它们不是必需的。我只是觉得在这种情况下它们增加了清晰度。
杰森·贝克

0

就我的观点而言,所有给定的选项都有两种易于理解的方法。因此,我的主要标准是具有非常易读的代码,而不是非常快速的代码。为了使代码易于理解,我更喜欢给定可能性:

  • var <= var2.keys()
  • var.issubset(var2)

在下面的测试中,“ var <= var2.keys()”的执行速度更快,这一事实我更喜欢。

import timeit

timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
0.1745898080000643

timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
0.2644960229999924

0

在确定是否只有某些键匹配的情况下,这可行:

any_keys_i_seek = ["key1", "key2", "key3"]

if set(my_dict).intersection(any_keys_i_seek):
    # code_here
    pass

查找是否只有一些键匹配的另一种选择:

any_keys_i_seek = ["key1", "key2", "key3"]

if any_keys_i_seek & my_dict.keys():
    # code_here
    pass

0

用于检测所有键是否都在字典中的另一个选项:

dict_to_test = { ... }  # dict
keys_sought = { "key_sought_1", "key_sought_2", "key_sought_3" }  # set

if keys_sought & dict_to_test.keys() == keys_sought: 
    # yes -- dict_to_test contains all keys in keys_sought
    # code_here
    pass

-4
>>> ok
{'five': '5', 'two': '2', 'one': '1'}

>>> if ('two' and 'one' and 'five') in ok:
...   print "cool"
... 
cool

这似乎有效


这很聪明,我坚信,直到我自己尝试一下它才起作用。我怀疑()首先会评估并得出结果True,然后再检查是否True in ok。这实际上如何工作?
durden2.0 '16

7
(“两个”,“一个”和“五个”)返回“五个”,因此它实际上仅检查“五个”是否符合要求
HardQuestions 2016年
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.