如何在一次传递中检查字典中是否有多个键?
- 2025-04-15 09:20:00
- admin 原创
- 38
问题描述:
我想做这样的事:
foo = {
'foo': 1,
'zip': 2,
'zam': 3,
'bar': 4
}
if ("foo", "bar") in foo:
#do stuff
如何检查foo
和是否bar
都在 dict 中foo
?
解决方案 1:
嗯,你可以这样做:
>>> if all(k in foo for k in ("foo","bar")):
... print "They're there!"
...
They're there!
解决方案 2:
if {"foo", "bar"} <= myDict.keys(): ...
如果你还在使用 Python 2,你可以这样做
if {"foo", "bar"} <= myDict.viewkeys(): ...
如果您仍在使用非常老的 Python <= 2.6,您可以调用set
该字典,但它会遍历整个字典来构建集合,这很慢:
if set(("foo", "bar")) <= set(myDict): ...
解决方案 3:
对 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:
你不必将左侧包裹在集合中。你可以这样做:
if {'foo', 'bar'} <= set(some_dict):
pass
这也比解决方案表现更好all(k in d...)
。
解决方案 5:
使用集合:
if set(("foo", "bar")).issubset(foo):
#do stuff
或者:
if set(("foo", "bar")) <= set(foo):
#do stuff
解决方案 6:
这应该可行:
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()
。
解决方案 7:
我认为这是最聪明、最 Pythonic 的。
{'key1','key2'} <= my_dict.keys()
解决方案 8:
虽然我喜欢 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'))
,因为它更短。但是,它不太容易理解,而且我觉得括号很容易让人误认为是字典。
解决方案 9:
检查字典中所有键是否存在:
{'key_1', 'key_2', 'key_3'} <= set(my_dict)
检查字典中是否存在一个或多个键:
{'key_1', 'key_2', 'key_3'} & set(my_dict)
解决方案 10:
Alex Martelli 的解决方案set(queries) <= set(my_dict)
是最短的代码,但未必是最快的。假设 Q = len(queries) 且 D = len(my_dict)。
这需要 O(Q) + O(D) 来构成两个集合,然后(希望如此!)只需要 O(min(Q,D)) 来进行子集测试 —— 当然假设 Python 集合查找是 O(1) —— 这是最坏的情况(当答案为 True 时)。
hughdbrown(等人)的生成器解决方案all(k in my_dict for k in queries)
是最坏情况的 O(Q)。
复杂因素:
(1)基于集合的小工具中的循环均以 C 速度完成,而基于任意的小工具则循环遍历字节码。
(2)基于任意的小工具的调用者可能能够使用任何失败概率知识来对查询项进行相应的排序,而基于集合的小工具则不允许进行此类控制。
与往常一样,如果速度很重要,那么在操作条件下进行基准测试是一个好主意。
解决方案 11:
您也可以使用.issubset()
>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>
解决方案 12:
简短而甜蜜
{"key1", "key2"} <= {*dict_name}
解决方案 13:
使用 lambda 怎么样?
if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
解决方案 14:
如果您想:
还可以获取键的值
查阅多个词典
然后:
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
解决方案 15:
并不是说你没有想到这一点,但我发现最简单的事情通常是最好的:
if ("foo" in foo) and ("bar" in foo):
# do stuff
解决方案 16:
>>> if 'foo' in foo and 'bar' in foo:
... print 'yes'
...
yes
Jason,() 在 Python 中不是必需的。
解决方案 17:
我个人认为,在所有给出的选项中,有两种方法比较容易理解。所以我的主要标准是代码可读性强,而不是运行速度特别快。为了保持代码易懂,我倾向于给出以下可能性:
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
解决方案 18:
在确定是否只有某些键匹配的情况下,这是有效的:
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
解决方案 19:
检测所有键是否都在字典中的另一种选择:
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
解决方案 20:
如果您想获取不匹配的物品,这里有一个替代解决方案......
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)
解决方案 21:
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")
解决方案 22:
对我来说,简单易用,中间有 None 键和pydash
ref
import pydash as _
_.get(d, 'key1.key2.key3.whatevermaybeNone.inthemiddle', default=None) )
解决方案 23:
如果您检查密钥的原因是为了确保您可以访问它们,那么一个选择就是不检查 - 只需尝试访问它们,然后在必要时执行必要的操作。
foo = {
'bar': 4
}
try:
alpha = foo['bar']
beta = foo['leroy_jenkins']
except KeyError:
pass # Or some other thing if you don't have the keys you expected.
解决方案 24:
根据我的测试Python 3.13.0
,这是最快的版本:
>>> foo = {
... 'foo': 1,
... 'zip': 2,
... 'zam': 3,
... 'bar': 4
... }
... for k in ('foo', 'bar'):
... if k not in foo:
... contains = False
... break
... else:
... contains = True
...
>>> contains
True
@John La Rooy's
使用答案来计时:
Python 3.13.0 (main, Nov 1 2024, 19:38:12) [Clang 16.0.0 (clang-1600.0.26.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) \nfor i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("\nlooking 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 632487
0.03017895808443427
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632308
9.833136573433876e-06
>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632107
3.875000402331352e-06
>>> Timer('''
... for k in q:
... if k not in d:
... in_ = False
... break
... else:
... in_ = True
... ''','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632327
2.3329630494117737e-06
解决方案 25:
>>> ok
{'five': '5', 'two': '2', 'one': '1'}
>>> if ('two' and 'one' and 'five') in ok:
... print "cool"
...
cool
这似乎有效
扫码咨询,免费领取项目管理大礼包!