我如何在一次传递中检查字典中是否有多个键?

我想做的事情是:

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


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

如何检查foobar是否都在字典foo中?

164558 次浏览

这应该可以工作:

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

提示:

all()内使用方括号创建一个列表推导式:

if all([key in foo for key in ["foo","bar"]]):

不仅是不必要的,而且是非常有害的,因为它们阻碍了all()的正常短路行为。

你可以这样做:

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

那么使用呢?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
if {"foo", "bar"} <= myDict.keys(): ...

如果你还在使用python2,你可以这样做

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

如果你仍然使用真的旧Python <= 2.6,你可以在字典上调用set,但它会遍历整个字典来构建集合,这是很慢的:

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

使用sets:

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

另外:

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

如果你想:

  • 还可以获取键的值
  • 多查字典

然后:

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

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

if ("foo" in foo) and ("bar" in foo):
# do stuff
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
...
yes

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

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时)。

hughdbrown (et al?) all(k in my_dict for k in queries)的生成器解是最差情况O(Q)。

< p >复杂的因素:< br > (1)基于set的小工具中的循环都是以c速度完成的,而基于any的小工具是在字节码上循环的 (2)基于任意的小工具的调用者可以使用任何关于失败概率的知识来对查询项进行相应的排序,而基于集的小工具则不允许这样的控制

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

简单的基准测试钻机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

你不需要把左边包在一个集合里。你可以这样做:

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

这也比all(k in d...)解决方案执行得更好。

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


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

这似乎有用

虽然我喜欢Alex Martelli的回答,但在我看来,它并不像python。也就是说,我认为Pythonic的一个重要部分是易于理解。有了这个目标,<=就不容易理解了。

虽然它有更多的字符,但使用Karl Voigtland的答案所建议的issubset()更容易理解。由于该方法可以使用字典作为参数,一个简短的、可理解的解决方案是:

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


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

我想用{'foo', 'bar'}来代替set(('foo', 'bar')),因为它更短。然而,这并不是那么容易理解的,我认为大括号太容易被混淆为字典。

我认为这是最聪明和最精辟的。

{'key1','key2'} <= my_dict.keys()

你也可以使用.issubset ()

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

这只是我的看法,在所有给定的选项中,有两个方法很容易理解。所以我的主要标准是代码可读性强,而不是特别快。为了保持代码的可理解性,我更喜欢给定的可能性:

  • Var <= Var 2.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

在确定是否只有一些键匹配的情况下,这是有效的:

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

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

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:
# True -- dict_to_test contains all keys in keys_sought
# code_here
pass

检查字典中是否存在所有键:

{'key_1', 'key_2', 'key_3'} <= set(my_dict)

检查字典中是否存在一个或多个键:

{'key_1', 'key_2', 'key_3'} & set(my_dict)

短而甜

{"key1", "key2"} <= {*dict_name}

这里有一个替代的解决方案,以防你想要得到不匹配的项目……

not_existing_keys = [item for item in ["foo","bar"] if item not in foo]
if not_existing_keys:
log.error('These items are missing', not_existing_keys)
my_dict = {
'name': 'Askavy',
'country': 'India',
'age': 30
}


if set(('name', 'country','age')).issubset(my_dict.keys()):
print("All keys are present in the dictionary")
else:
print("All keys are not present in  the dictionary")

对我来说,简单和容易与None键在中间与pydash 裁判

import pydash as _
_.get(d, 'key1.key2.key3.whatevermaybeNone.inthemiddle', default=None) )