Python : 한 사전이 다른 더 큰 사전의 하위 집합인지 확인
임의의 수의 kwargs 를 사용하고 해당 kwargs 를 포함하는 데이터베이스와 같은 목록의 요소를 포함하는 목록을 반환 하는 사용자 지정 필터 메서드를 작성하려고합니다 .
예를 들어, d1 = {'a':'2', 'b':'3'}
and d2
= 같은 것을 가정 하십시오. d1 == d2
True가됩니다. 그러나 d2
= 같은 것 + 다른 것들이 있다고 가정 하십시오. 내 방법 은 d2의 d1 을 알 수 있어야 하지만 파이썬은 사전으로 그렇게 할 수 없습니다.
문맥:
Word 클래스가 있고 각 개체에는 word
,, definition
등의 속성이 있습니다 part_of_speech
. 이 단어의 기본 목록에서 필터 메서드를 호출 할 수 있기를 원합니다 Word.objects.filter(word='jump', part_of_speech='verb-intransitive')
. 이 키와 값을 동시에 관리하는 방법을 알 수 없습니다. 그러나 이것은 다른 사람들을 위해이 컨텍스트 밖에서 더 큰 기능을 가질 수 있습니다.
항목 쌍으로 변환하고 격리를 확인하십시오.
all(item in superset.items() for item in subset.items())
최적화는 독자를위한 연습으로 남겨집니다.
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)
단위 테스트를 위해 이것을 필요로하는 사람들을위한 참고 사항 : assertDictContainsSubset()
Python의 TestCase
클래스 에도 메서드가 있습니다.
그러나 3.2에서는 더 이상 사용되지 않으며 이유가 확실하지 않으며 대체가있을 수 있습니다.
키 및 값 확인 사용 : set(d1.items()).issubset(set(d2.items()))
키만 확인해야하는 경우 : set(d1).issubset(set(d2))
완전성을 위해 다음을 수행 할 수도 있습니다.
def is_subdict(small, big):
return dict(big, **small) == big
그러나 나는 속도 (또는 속도 부족) 또는 가독성 (또는 부족)과 관련하여 어떠한 주장도하지 않습니다.
>>> 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 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
귀하의 예에서 dictMatch(d1, d2)
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
절 을 피하고 더 넓은 범위의 경우 (해시 목록 등)에 적용 하는 더 나은 솔루션이있을 수 있습니다 . 또한 재귀는 여기에 제한되지 않으므로 자신의 책임하에 사용하십시오. ;)
이 겉보기에 간단 해 보이는 문제는 100 % 신뢰할 수있는 솔루션을 찾기 위해 연구하는 데 몇 시간이 걸리므로이 답변에서 찾은 내용을 문서화했습니다.
"Pythonic-ally"
small_dict <= big_dict
는 가장 직관적 인 방법이지만 작동하지 않을 정도로 나쁘다 .{'a': 1} < {'a': 1, 'b': 2}
겉보기에는 Python 2에서 작동하지만 공식 문서에서 명시 적으로 언급하기 때문에 신뢰할 수 없습니다. Go search "평등 이외의 결과는 일관되게 해결되지만 달리 정의되지 않았습니다." 에서 이 섹션 . 말할 것도없이 Python 3에서 2 개의 사전을 비교하면 TypeError 예외가 발생합니다.The second most-intuitive thing is
small.viewitems() <= big.viewitems()
for Python 2.7 only, andsmall.items() <= big.items()
for Python 3. But there is one caveat: it is potentially buggy. If your program could potentially be used on Python <=2.6, itsd1.items() <= d2.items()
are actually comparing 2 lists of tuples, without particular order, so the final result will be unreliable and it becomes a nasty bug in your program. I am not keen to write yet another implementation for Python<=2.6, but I still don't feel comfortable that my code comes with a known bug (even if it is on an unsupported platform). So I abandon this approach.I settle down with @blubberdiblub 's answer (Credit goes to him):
def is_subdict(small, big): return dict(big, **small) == big
이 답변은
==
공식 문서에 명확하게 정의되어 있으므로 모든 Python 버전에서 작동해야하는 dict 간의 동작에 의존한다는 점을 지적 할 가치가 있습니다. 검색하기 :
주어진 문제에 대한 일반적인 재귀 솔루션은 다음과 같습니다.
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()
참고 : 원래 코드는 어떤 경우에는 실패의 학점 정부는 에 간다 @ 올리비에 - melançon
I know this question is old, but here is my solution for checking if one nested dictionary is a part of another nested dictionary. The solution is recursive.
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
This function works for non-hashable values. I also think that it is clear and easy to read.
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
If you don't mind using pydash there is is_match there which does exactly that:
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
A short recursive implementation that works for nested dictionaries:
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
This will consume the a and b dicts. If anyone knows of a good way to avoid that without resorting to partially iterative solutions as in other answers, please tell me. I would need a way to split a dict into head and tail based on a key.
This code is more usefull as a programming exercise, and probably is a lot slower than other solutions in here that mix recursion and iteration. @Nutcracker's solution is pretty good for nested dictionaries.
Here is a solution that also properly recurses into lists and sets contained within the dictionary. You can also use this for lists containing dicts etc...
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
'Programing' 카테고리의 다른 글
Google Maps API v3 : 이벤트 리스너를 제거하는 방법? (0) | 2020.09.11 |
---|---|
Apache POI Excel-확장 할 열을 구성하는 방법은 무엇입니까? (0) | 2020.09.11 |
jQuery는 데이터 속성 값으로 요소 찾기 (0) | 2020.09.11 |
helm list :“kube-system”네임 스페이스에있는 configmap을 나열 할 수 없습니다. (0) | 2020.09.11 |
ipython 노트북에서 matplotlib 그림 기본 크기를 설정하는 방법은 무엇입니까? (0) | 2020.09.11 |