Python:检查一个字典是否是另一个更大的字典的一个子集
我试图编写一个自定义的filter方法,它接受任意数量的kwargs,并返回一个包含类似数据库的列表中包含这些kwargs的元素的列表。
例如,假设d1 = {'a':'2', 'b':'3'}
和d2
=相同的东西。 d1 == d2
导致为真。 但是,假设d2
=相同的东西加上一堆其他的东西。 我的方法需要能够告诉d1是否在d2中 ,但Python不能用字典来实现。
语境:
我有一个Word类,每个对象都有像word
, definition
, part_of_speech
等属性。 我想能够在这些单词的主要列表Word.objects.filter(word='jump', part_of_speech='verb-intransitive')
滤器方法,如Word.objects.filter(word='jump', part_of_speech='verb-intransitive')
。 我无法弄清楚如何同时pipe理这些键和值。 但是对于其他人来说,这可能在这个环境之外具有更大的function
转换成物品对并检查包容。
all(item in superset.items() for item in subset.items())
优化是作为读者的练习。
请注意,需要进行unit testing的人:Python的TestCase
类中还有一个assertDictContainsSubset()
方法。
但在3.2中已经弃用了,不知道为什么,也许有替代品。
对于键和值检查使用: set(d1.items()).issubset(set(d2.items()))
如果您只需要检查密钥: set(d1).issubset(set(d2))
在Python 3中,您可以使用dict.items()
来获取字典项目的集合式视图。 然后可以使用<=
运算符来testing一个视图是否是另一个视图的“子集”:
d1.items() <= d2.items()
在Python 2.7中,使用dict.viewitems()
来执行相同的操作:
d1.viewitems() <= d2.viewitems()
在Python 2.6及更低版本中,您将需要一个不同的解决scheme,例如使用all()
:
all(key in d2 and d2[key] == d1[key] for key in d1)
>>> d1 = {'a':'2', 'b':'3'} >>> d2 = {'a':'2', 'b':'3','c':'4'} >>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems()) True
背景:
>>> d1 = {'a':'2', 'b':'3'} >>> d2 = {'a':'2', 'b':'3','c':'4'} >>> list(d1.iteritems()) [('a', '2'), ('b', '3')] >>> [(k,v) for k,v in d1.iteritems()] [('a', '2'), ('b', '3')] >>> k,v = ('a','2') >>> k 'a' >>> v '2' >>> k in d2 True >>> d2[k] '2' >>> k in d2 and d2[k]==v True >>> [(k in d2 and d2[k]==v) for k,v in d1.iteritems()] [True, True] >>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems()) <generator object <genexpr> at 0x02A9D2B0> >>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems()).next() True >>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems()) True >>>
为了完整性,你也可以这样做:
def is_subdict(small, big): return dict(big, **small) == big
但是,我对速度(或缺乏)或可读性(或缺乏)没有任何声明。
我的function为同样的目的,recursion地做这件事:
def dictMatch(patn, real): """does real dict match pattern?""" try: for pkey, pvalue in patn.iteritems(): if type(pvalue) is dict: result = dictMatch(pvalue, real[pkey]) else: assert real[pkey] == pvalue result = True except (AssertionError, KeyError): result = False return result
在你的例子中, dictMatch(d1, d2)
应该返回True,即使d2中有其他东西,再加上它也适用于较低级别:
d1 = {'a':'2', 'b':{3: 'iii'}} d2 = {'a':'2', 'b':{3: 'iii', 4: 'iv'},'c':'4'} dictMatch(d1, d2) # True
注:可以有更好的解决scheme,避免if type(pvalue) is dict
条款,适用于更广泛的案件范围(如哈希等列表)。 此外,recursion不限于此,请自行承担风险。 ;)
这个看似简单的问题花了我几个小时的研究,find一个100%可靠的解决scheme,所以我logging了我在这个答案中find了什么。
-
“Pythonic-
small_dict <= big_dict
”说,small_dict <= big_dict
将是最直观的方式,但太糟糕了, 它将无法正常工作 。{'a': 1} < {'a': 1, 'b': 2}
似乎可以在Python 2中使用,但是它并不可靠,因为官方文档明确地调用了它。 去search“除了平等以外的结果一致解决,但没有其他定义。” 在本节中 。 更不用说,比较Python 3中的2个字节会导致TypeErrorexception。 -
第二个最直观的事情是Python 2.7中的
small.items() <= big.items()
,Python 3中的small.items() <= big.items()
。但是有一点需要注意: 越野车 。 如果你的程序可能在Python <= 2.6上使用,那么它的d1.items() <= d2.items()
实际上比较了2个元组列表,没有特别的顺序,所以最后的结果是不可靠的,变成了一个讨厌的错误在您的程序中。 我不想为Python <= 2.6编写另一个实现,但是我的代码还是带有一个已知的错误(即使它在不受支持的平台上),我仍然觉得不舒服。 所以我放弃了这个方法。 -
我用@blubberdiblub的答案解决了问题 (信用卡给他):
def is_subdict(small, big): return dict(big, **small) == big
值得指出的是,这个答案依赖于在官方文档中明确定义的字典之间的
==
行为,因此应该在每个Python版本中工作 。 去search:- “当且仅当它们具有相同(键,值)对时,字典比较相等”。 是本页最后一句话
- “当且仅当它们具有相等的(键,值)对时,映射(dict的实例)比较相等。键和元素的相等比较强制reflection性。 在这个页面
此函数适用于不可哈希值。 我也认为这是清楚和容易阅读。
def isSubDict(subDict,dictionary): for key in subDict.keys(): if (not key in dictionary) or (not subDict[key] == dictionary[key]): return False return True In [126]: isSubDict({1:2},{3:4}) Out[126]: False In [127]: isSubDict({1:2},{1:2,3:4}) Out[127]: True In [128]: isSubDict({1:{2:3}},{1:{2:3},3:4}) Out[128]: True In [129]: isSubDict({1:{2:3}},{1:{2:4},3:4}) Out[129]: False