我正在尝试编写一个自定义过滤器方法,它接受任意数量的 kwargs 并返回一个列表,其中包含类似数据库的列表,其中包含那些 kwargs
例如,假设d1 = {'a':'2', 'b':'3'}
和d2
=同样的事情。 d1 == d2
结果为True。但是假设d2
=同样的事情加上一堆其他的东西。我的方法需要能够判断d2中的 d1 ,但Python不能用字典来表示。
上下文:
我有一个Word类,每个对象都有word
,definition
,part_of_speech
等属性。我希望能够在这些单词的主列表上调用过滤方法,例如Word.objects.filter(word='jump', part_of_speech='verb-intransitive')
。我无法弄清楚如何同时管理这些键和值。但是,对于其他人来说,这可能会在此背景下具有更大的功能。
答案 0 :(得分:85)
转换为项目对并检查包含。
all(item in superset.items() for item in subset.items())
优化留给读者练习。
答案 1 :(得分:57)
在Python 3中,您可以使用dict.items()
获取dict项目的类似集合的视图。然后,您可以使用<=
运算符来测试一个视图是否是另一个视图的“子集”:
d1.items() <= d2.items()
在Python 2.7中,使用dict.viewitems()
执行相同的操作:
d1.viewitems() <= d2.viewitems()
在Python 2.6及更低版本中,您需要一个不同的解决方案,例如使用all()
:
all(key in d2 and d2[key] == d1[key] for key in d1)
答案 2 :(得分:31)
请注意那些需要单元测试的人:Python的assertDictContainsSubset()
类中还有一个TestCase
方法。
然而,它在3.2中被弃用,不知道为什么,也许有替代它。
答案 3 :(得分:20)
用于键和值检查使用:
set(d1.items()).issubset(set(d2.items()))
如果您只需要检查键:
set(d1).issubset(set(d2))
答案 4 :(得分:15)
为完整起见,您也可以这样做:
def is_subdict(small, big):
return dict(big, **small) == big
但是,我对速度(或缺乏速度)或可读性(或缺乏速度)没有任何声明。
答案 5 :(得分:10)
>>> 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
>>>
答案 6 :(得分:4)
我的功能是出于同样的目的,递归地执行此操作:
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])
assert result
else:
assert real[pkey] == pvalue
result = True
except (AssertionError, KeyError):
result = False
return result
在您的示例中,即使d2中包含其他内容,dictMatch(d1, d2)
也应返回True,此外它也适用于较低级别:
d1 = {'a':'2', 'b':{3: 'iii'}}
d2 = {'a':'2', 'b':{3: 'iii', 4: 'iv'},'c':'4'}
dictMatch(d1, d2) # True
注意:可能有更好的解决方案可以避免if type(pvalue) is dict
子句,并适用于更广泛的案例(如哈希列表等)。此外递归不限于此,因此使用风险自负。 ;)
答案 7 :(得分:2)
这是给出问题的一般递归解决方案:
import traceback
import unittest
def is_subset(superset, subset):
for key, value in subset.items():
if key not in superset:
return False
if isinstance(value, dict):
if not is_subset(superset[key], value):
return False
elif isinstance(value, str):
if value not in superset[key]:
return False
elif isinstance(value, list):
if not set(value) <= set(superset[key]):
return False
elif isinstance(value, set):
if not value <= superset[key]:
return False
else:
if not value == superset[key]:
return False
return True
class Foo(unittest.TestCase):
def setUp(self):
self.dct = {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
'f': {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
'g': False,
'h': None
},
'g': False,
'h': None,
'question': 'mcve',
'metadata': {}
}
def tearDown(self):
pass
def check_true(self, superset, subset):
return self.assertEqual(is_subset(superset, subset), True)
def check_false(self, superset, subset):
return self.assertEqual(is_subset(superset, subset), False)
def test_simple_cases(self):
self.check_true(self.dct, {'a': 'hello world'})
self.check_true(self.dct, {'b': 12345})
self.check_true(self.dct, {'c': 1.2345})
self.check_true(self.dct, {'d': [1, 2, 3, 4, 5]})
self.check_true(self.dct, {'e': {1, 2, 3, 4, 5}})
self.check_true(self.dct, {'f': {
'a': 'hello world',
'b': 12345,
'c': 1.2345,
'd': [1, 2, 3, 4, 5],
'e': {1, 2, 3, 4, 5},
}})
self.check_true(self.dct, {'g': False})
self.check_true(self.dct, {'h': None})
def test_tricky_cases(self):
self.check_true(self.dct, {'a': 'hello'})
self.check_true(self.dct, {'d': [1, 2, 3]})
self.check_true(self.dct, {'e': {3, 4}})
self.check_true(self.dct, {'f': {
'a': 'hello world',
'h': None
}})
self.check_false(
self.dct, {'question': 'mcve', 'metadata': {'author': 'BPL'}})
self.check_true(
self.dct, {'question': 'mcve', 'metadata': {}})
self.check_false(
self.dct, {'question1': 'mcve', 'metadata': {}})
if __name__ == "__main__":
unittest.main()
注意:原始代码在某些情况下会失败,fixing的信用转为@olivier-melançon
答案 8 :(得分:2)
这里是一种解决方案,也可以正确地递归到词典中包含的列表和集合中。您也可以将其用于包含字典等的列表...
def is_subset(subset, superset):
if isinstance(subset, dict):
return all(key in superset and is_subset(val, superset[key]) for key, val in subset.items())
if isinstance(subset, list) or isinstance(subset, set):
return all(any(is_subset(subitem, superitem) for superitem in superset) for subitem in subset)
# assume that subset is a plain value if none of the above match
return subset == superset
答案 9 :(得分:2)
我知道这个问题很旧,但是这是我的解决方案,用于检查一个嵌套字典是否是另一个嵌套字典的一部分。解决方案是递归的。
def compare_dicts(a, b):
for key, value in a.items():
if key in b:
if isinstance(a[key], dict):
if not compare_dicts(a[key], b[key]):
return False
elif value != b[key]:
return False
else:
return False
return True
答案 10 :(得分:2)
这个看似简单的问题花了我几个小时的研究来找到100%可靠的解决方案,所以我记录了我在这个答案中找到的内容。
&#34; Python化烯丙基&#34;说起来,small_dict <= big_dict
将是最直观的方式,但太糟糕了,赢得了 。 {'a': 1} < {'a': 1, 'b': 2}
似乎在Python 2中有效,但它不可靠,因为官方文档明确地将其命名。去搜索&#34;除了相等之外的结果一致地解决,但没有另外定义。&#34;在this section。更不用说,比较Python 3中的2个dicts会导致TypeError异常。
第二个最直观的东西是仅用于Python 2.7的small.viewitems() <= big.viewitems()
和用于Python 3的small.items() <= big.items()
。但有一点需要注意:它是潜在的错误 。如果您的程序可能在Python&lt; = 2.6上使用,那么它的d1.items() <= d2.items()
实际上是在比较2个元组列表,没有特定的顺序,所以最终的结果将是不可靠的,它会成为程序中的一个讨厌的错误。我并不热衷于为Python&lt; = 2.6编写另一个实现,但我仍然不觉得我的代码带有已知错误(即使它位于不受支持的平台上)。所以我放弃了这种方法。
我和@blubberdiblub 's answer安定下来(信用证告诉他):
def is_subdict(small, big):
return dict(big, **small) == big
值得指出的是,这个答案依赖于dicts之间的==
行为,这在官方文档中有明确定义,因此应该适用于每个Python版本。去搜索:
答案 11 :(得分:1)
另一种方法:
>>> d1 = {'a':'2', 'b':'3'}
>>> d2 = {'a':'2', 'b':'3','c':'4'}
>>> d3 = {'a':'1'}
>>> set(d1.items()).issubset(d2.items())
True
>>> set(d3.items()).issubset(d2.items())
False
答案 12 :(得分:1)
对于 Python 3.9,这就是我使用的:
def dict_contains_dict(small: dict, big: dict):
return (big | small) == big
答案 13 :(得分:0)
如果您不介意使用pydash,那么那里的is_match就是这样:
import pydash
a = {1:2, 3:4, 5:{6:7}}
b = {3:4.0, 5:{6:8}}
c = {3:4.0, 5:{6:7}}
pydash.predicates.is_match(a, b) # False
pydash.predicates.is_match(a, c) # True
答案 14 :(得分:0)
适用于嵌套字典的简短递归实现:
def compare_dicts(a,b):
if not a: return True
if isinstance(a, dict):
key, val = a.popitem()
return isinstance(b, dict) and key in b and compare_dicts(val, b.pop(key)) and compare_dicts(a, b)
return a == b
这将消耗a和b字典。如果有人知道避免这种情况的好方法,而又不像其他答案那样求助于部分迭代的解决方案,请告诉我。我需要一种基于键将字典拆分为头部和尾部的方法。
此代码作为编程练习更有用,并且可能比此处混合递归和迭代的其他解决方案慢很多。 @Nutcracker's solution非常适合嵌套字典。
答案 15 :(得分:0)
此函数适用于不可散列的值。我也认为它清晰易读。
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
答案 16 :(得分:0)
使用此包装器对象提供部分比较和出色的差异:
class DictMatch(dict):
""" Partial match of a dictionary to another one """
def __eq__(self, other: dict):
assert isinstance(other, dict)
return all(other[name] == value for name, value in self.items())
actual_name = {'praenomen': 'Gaius', 'nomen': 'Julius', 'cognomen': 'Caesar'}
expected_name = DictMatch({'praenomen': 'Gaius'}) # partial match
assert expected_name == actual_name # True
答案 17 :(得分:0)
如果在字典中有一些其他字典数组,则大多数答案将不起作用,这是解决此问题的方法:
def d_eq(d, d1):
if not isinstance(d, (dict, list)):
return d == d1
if isinstance(d, list):
return all(d_eq(a, b) for a, b in zip(d, d1))
return all(d.get(i) == d1[i] or d_eq(d.get(i), d1[i]) for i in d1)
def is_sub(d, d1):
if isinstance(d, list):
return any(is_sub(i, d1) for i in d)
return d_eq(d, d1) or (isinstance(d, dict) and any(is_sub(b, d1) for b in d.values()))
print(is_sub(dct_1, dict_2))