Python:检查一个字典是否是另一个更大的字典的一个子集

我试图编写一个自定义的filter方法,它接受任意数量的kwargs,并返回一个包含类似数据库的列表中包含这些kwargs的元素的列表。

例如,假设d1 = {'a':'2', 'b':'3'}d2 =相同的东西。 d1 == d2导致为真。 但是,假设d2 =相同的东西加上一堆其他的东西。 我的方法需要能够告诉d1是否在d2中 ,但Python不能用字典来实现。

语境:

我有一个Word类,每个对象都有像worddefinitionpart_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()方法。

http://docs.python.org/2/library/unittest.html?highlight=assertdictcontainssubset#unittest.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了什么。

  1. “Pythonic- small_dict <= big_dict ”说, small_dict <= big_dict将是最直观的方式,但太糟糕了, 它将无法正常工作{'a': 1} < {'a': 1, 'b': 2}似乎可以在Python 2中使用,但是它并不可靠,因为官方文档明确地调用了它。 去search“除了平等以外的结果一致解决,但没有其他定义。” 在本节中 。 更不用说,比较Python 3中的2个字节会导致TypeErrorexception。

  2. 第二个最直观的事情是Python 2.7中的small.items() <= big.items() ,Python 3中的small.items() <= big.items() 。但是有一点需要注意: 越野车 。 如果你的程序可能在Python <= 2.6上使用,那么它的d1.items() <= d2.items()实际上比较了2个元组列表,没有特别的顺序,所以最后的结果是不可靠的,变成了一个讨厌的错误在您的程序中。 我不想为Python <= 2.6编写另一个实现,但是我的代码还是带有一个已知的错误(即使它在不受支持的平台上),我仍然觉得不舒服。 所以我放弃了这个方法。

  3. 我用@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