programing

한 사전이 다른 더 큰 사전의 하위 집합인지 확인하는 방법은 무엇입니까?

abcjava 2023. 6. 15. 21:30
반응형

한 사전이 다른 더 큰 사전의 하위 집합인지 확인하는 방법은 무엇입니까?

임의의 수의 kwarg를 가져다가 해당 kwarg를 포함하는 데이터베이스와 유사한 목록의 요소를 포함하는 목록을 반환하는 사용자 정의 필터 방법을 작성하려고 합니다.

를 들어, 를들어예,어들▁suppose▁for,d1 = {'a':'2', 'b':'3'}그리고.d2같은 것 d1 == d2결과가 True가 됩니다.하지만 예를 들어d2같은 것에 다른 많은 것들을 더한 것들.내 방법은 d2의 d1을 구별할 수 있어야 하지만, 파이썬은 사전으로 그렇게 할 수 없습니다.

컨텍스트:

저는 클래스가 , 는 워드 클가있각고개다체음같가속집다니은성을과는래스▁wordties▁like와 같은 속성을 가지고 .word,definition,part_of_speech등등.나는 이 단어들의 주요 목록에 있는 필터 메소드를 부를 수 있기를 원합니다.Word.objects.filter(word='jump', part_of_speech='verb-intransitive')이 키와 값을 동시에 관리하는 방법을 모르겠습니다.하지만 이것은 다른 사람들을 위해 이 맥락 밖에서 더 큰 기능을 가질 수 있습니다.

3에서는 3을 사용할 수 .dict.items()딕트 항목을 세트처럼 표시합니다.그러면 다음을 사용할 수 있습니다.<= 뷰가 뷰의 인지 테스트하는 : " 한 부 " 분 " 할 " 산 인 " 자 " 연 " subset " 트 " 스 " 지 " 테 " 의 "

d1.items() <= d2.items()

2에서는 Python 2.7을 합니다.에서는dict.viewitems()동일한 작업 수행:

d1.viewitems() <= d2.viewitems()

2에서는 Python 2.6을 사용하는 등 합니다.all():

all(key in d2 and d2[key] == d1[key] for key in d1)

항목 쌍으로 변환하고 격납 여부를 확인합니다.

all(item in superset.items() for item in subset.items())

최적화는 독자를 위한 연습으로 남겨집니다.

장치 테스트를 위해 이것이 필요한 사람들을 위한 참고 사항: 또한 있습니다.assertDictContainsSubset()파이메드에 TestCase학생들

http://docs.python.org/2/library/unittest.html?highlight=assertdictcontainssubset#unittest.TestCase.assertDictContainsSubset

그러나 3.2에서는 더 이상 사용되지 않습니다. 그 이유는 확실하지 않습니다. 대신 사용할 수도 있습니다.

완전성을 위해 다음 작업도 수행할 수 있습니다.

def is_subdict(small, big):
    return dict(big, **small) == big

그러나 속도(또는 속도의 부족) 또는 가독성(또는 부족)과 관련하여 어떠한 주장도 하지 않습니다.

업데이트: Boris의 코멘트에서 지적했듯이, 이 트릭은 당신의 작은 딕트에 문자열이 아닌 키가 있고 당신이 Python >= 3을 사용하고 있다면 작동하지 않습니다(다시 말해, 임의로 입력한 키 앞에서는, 레거시 Python 2.x에서만 작동합니다).

그러나 Python 3.9 이상을 사용하는 경우 문자열이 아닌 입력된 키 모두에서 작동할 수 있을 뿐만 아니라 더 깔끔한 구문을 얻을 수 있습니다.

코드에 이미 두 개의 사전이 변수로 포함되어 있는 경우 이 인라인을 확인하는 것이 매우 간단합니다.

if big | small == big:
    # do something

그렇지 않은 경우 또는 위와 같이 재사용 가능한 기능을 선호하는 경우 다음을 사용할 수 있습니다.

def is_subdict(small, big):
    return big | small == big

작동 원리는 첫 번째 기능과 동일하지만, 이번에는 딕트를 지원하도록 확장된 조합 운영자를 사용하는 것과 관련이 있습니다.

키 및 값을 확인하려면 다음을 사용합니다.set(d1.items()).issubset(set(d2.items()))

키만확하는경우야인:set(d1).issubset(set(d2))

>>> 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_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

python 3.10을 사용할 때 python의 새로운 match 문을 사용하여 유형 검사를 수행할 수 있습니다.

def is_subset(subset, superset):
    match subset:
        case dict(_): return all(key in superset and is_subset(val, superset[key]) for key, val in subset.items())
        case list(_) | 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
        case _: return subset == superset

Python 3.9에서는 다음과 같이 사용합니다.

def dict_contains_dict(small: dict, big: dict):    
   return (big | small) == big

동일한 목적을 위한 내 기능, 재귀적으로 수행:

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 반 야 하 수 위 준 에 도 적 됩 용 니 다 하 며 해 환 라 더 도 어 이 되 함 됩 ▁should 니 다 ▁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더 광범위한 사례(해시 목록 등)에 적용됩니다.또한 재귀는 여기에 제한되지 않으므로 위험을 감수하고 사용하십시오.;)

괜찮으시다면 사용하세요.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

겉보기에는 간단해 보이는 이 문제로 인해 100% 신뢰할 수 있는 솔루션을 찾기 위해 몇 시간의 연구 시간이 소요되므로 이 답변에서 발견한 내용을 문서화했습니다.

  1. "파이토닉 아군"입니다.small_dict <= big_dict가장 직관적인 방법이겠지만, 작동하지 않는 것이 유감입니다. {'a': 1} < {'a': 1, 'b': 2}겉보기에는 Python 2에서 작동하지만 공식 문서에서 명시적으로 호출하기 때문에 신뢰할 수 없습니다.이동에서 "평등 이외의 결과는 일관되게 해결되지만 정의되지는 않습니다."를 검색합니다.말할 것도 없이 Python 3에서 2개의 딕트를 비교하면 TypeError 예외가 발생합니다.

  2. 은 두번로직인것은적입니다.small.viewitems() <= big.viewitems() 2 및 Python 2.7 용small.items() <= big.items()Python 3용.하지만 한 가지 주의할 점이 있습니다: 그것은 잠재적으로 버그가 있습니다.당신의 프로그램이 잠재적으로 파이썬 <=2.6에서 사용될 수 있다면, 그것은d1.items() <= d2.items()실제로 특별한 순서 없이 튜플의 두 목록을 비교하고 있기 때문에 최종 결과는 신뢰할 수 없을 것이고 당신의 프로그램에서 끔찍한 버그가 될 것입니다.파이썬<=2.6을 위한 다른 구현체를 작성하고 싶지 않지만, 제 코드가 알려진 버그와 함께 제공된다는 것이 여전히 불편합니다(지원되지 않는 플랫폼에 있더라도).그래서 저는 이 접근법을 포기합니다.

  3. 저는 @blubberdiblub의 대답에 안주합니다(신용은 그에게 있습니다).

    def is_subdict(small, big): return dict(big, **small) == big

    이 대답은 다음에 의존한다는 것을 지적할 가치가 있습니다.==딕트 간의 동작은 공식 문서에 명확하게 정의되어 있으므로 모든 파이썬 버전에서 작동해야 합니다.이동 검색:

    • "사전은 동일한 (키, 값) 쌍을 가지고 있는 경우에만 동일하게 비교합니다."가 이 페이지의 마지막 문장입니다.
    • "매핑(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()

참고: 경우에 따라 원래 코드가 실패할 수 있습니다. 수정에 대한 크레딧은 @olivier-melancon에게 전달됩니다.

이 질문이 오래된 질문인 것은 알지만, 중첩된 사전이 다른 중첩된 사전의 일부인지 확인하기 위한 해결책이 있습니다.솔루션은 재귀적입니다.

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

이를 위한 다른 방법:

>>> 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

부분 비교 및 nice diff를 제공하는 이 래퍼 개체를 사용합니다.


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

딕트 내에 다른 딕트 배열이 있는 경우 대부분의 답변이 작동하지 않습니다. 이에 대한 해결책은 다음과 같습니다.

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))

taked from dict가 다른 복잡한 dict의 하위 집합인지 확인하는 방법

이 함수는 해시할 수 없는 값에 대해 작동합니다.저는 또한 그것이 분명하고 읽기 쉽다고 생각합니다.

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

중첩된 사전에 대해 작동하는 짧은 재귀 구현:

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 및 bdt가 소모됩니다.다른 답변처럼 부분적으로 반복적인 해결책에 의존하지 않고 그것을 피할 수 있는 좋은 방법을 아는 사람이 있다면 저에게 말해주십시오.저는 딕트를 키를 기반으로 머리와 꼬리로 나누는 방법이 필요합니다.

이 코드는 프로그래밍 연습으로 더 유용하며, 재귀와 반복을 혼합하는 다른 솔루션보다 훨씬 느릴 수 있습니다.@호두까기 인형의 솔루션은 중첩된 사전에 적합합니다.

언급URL : https://stackoverflow.com/questions/9323749/how-to-check-if-one-dictionary-is-a-subset-of-another-larger-dictionary

반응형