복붙노트

[PYTHON] 다양한 깊이의 중첩 된 사전 값 업데이트

PYTHON

다양한 깊이의 중첩 된 사전 값 업데이트

나는 dict dictionary1을 dict 업데이트의 내용으로 업데이트하는 방법을 찾고있다. levelA를 덮어 쓰지 않는다.

dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}

가장 낮은 키 level1을 업데이트하기 때문에 update2가 level2의 값을 삭제한다는 것을 알고 있습니다.

dictionary1과 update가 길이를 가질 수 있다면 어떻게해야합니까?

해결법

  1. ==============================

    1.@ FM의 대답은 재귀 적 솔루션 인 올바른 일반 아이디어를 가지고 있지만 다소 특이한 코딩과 적어도 하나의 버그가 있습니다. 대신 권하고 싶습니다.

    @ FM의 대답은 재귀 적 솔루션 인 올바른 일반 아이디어를 가지고 있지만 다소 특이한 코딩과 적어도 하나의 버그가 있습니다. 대신 권하고 싶습니다.

    파이썬 2 :

    import collections
    
    def update(d, u):
        for k, v in u.iteritems():
            if isinstance(v, collections.Mapping):
                d[k] = update(d.get(k, {}), v)
            else:
                d[k] = v
        return d
    

    파이썬 3 :

    import collections
    
    def update(d, u):
        for k, v in u.items():
            if isinstance(v, collections.Mapping):
                d[k] = update(d.get(k, {}), v)
            else:
                d[k] = v
        return d
    

    버그는 "업데이트"에 ak가있을 때 나타납니다. v 항목은 v가 사전이고 k가 원래 사전에있는 키가 아닙니다. @ FM의 코드는 업데이트의이 부분을 "건너 뜁니다" 저장되지 않거나 어디에서나 반환되는 빈 새 dict, 재귀 호출이 반환 될 때 손실 됨).

    내 다른 변경 사항은 중요하지 않습니다 .get이 동일한 작업을 더 빠르고 깨끗하게 수행 할 때 if / else 구문을 사용할 이유가 없으며 isinstance가 일반 기본 클래스 (구체적이 아닌)에 가장 잘 적용됩니다.

  2. ==============================

    2.이 점에 대해 조금 생각했지만, @ Alex의 덕분에 그는 내가 누락 된 틈을 메웠다. 그러나 반복적 인 dict 내의 값이 목록 일 경우 문제가 발생하여 공유하고 응답을 연장한다고 생각했습니다.

    이 점에 대해 조금 생각했지만, @ Alex의 덕분에 그는 내가 누락 된 틈을 메웠다. 그러나 반복적 인 dict 내의 값이 목록 일 경우 문제가 발생하여 공유하고 응답을 연장한다고 생각했습니다.

    import collections
    
    def update(orig_dict, new_dict):
        for key, val in new_dict.iteritems():
            if isinstance(val, collections.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict.get(key, []) + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict
    
  3. ==============================

    3.@ Alex의 답은 좋지만 update ({ 'foo': 0}, { 'foo': { 'bar': 1}})와 같은 정수로 요소를 대체 할 때는 작동하지 않습니다. 이 업데이트는이 문제를 해결합니다.

    @ Alex의 답은 좋지만 update ({ 'foo': 0}, { 'foo': { 'bar': 1}})와 같은 정수로 요소를 대체 할 때는 작동하지 않습니다. 이 업데이트는이 문제를 해결합니다.

    import collections
    def update(d, u):
        for k, v in u.iteritems():
            if isinstance(d, collections.Mapping):
                if isinstance(v, collections.Mapping):
                    r = update(d.get(k, {}), v)
                    d[k] = r
                else:
                    d[k] = u[k]
            else:
                d = {k: u[k]}
        return d
    
    update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
    
  4. ==============================

    4.깊이가 다른 사전을 업데이트하고 업데이트가 원래 중첩 된 사전으로 잠수하는 깊이를 제한하는 @ Alex의 대답에 대한 사소한 개선 (업데이트 사전 깊이는 제한되지 않음). 몇 가지 경우 만 테스트되었습니다.

    깊이가 다른 사전을 업데이트하고 업데이트가 원래 중첩 된 사전으로 잠수하는 깊이를 제한하는 @ Alex의 대답에 대한 사소한 개선 (업데이트 사전 깊이는 제한되지 않음). 몇 가지 경우 만 테스트되었습니다.

    def update(d, u, depth=-1):
        """
        Recursively merge or update dict-like objects. 
        >>> update({'k1': {'k2': 2}}, {'k1': {'k2': {'k3': 3}}, 'k4': 4})
        {'k1': {'k2': {'k3': 3}}, 'k4': 4}
        """
    
        for k, v in u.iteritems():
            if isinstance(v, Mapping) and not depth == 0:
                r = update(d.get(k, {}), v, depth=max(depth - 1, -1))
                d[k] = r
            elif isinstance(d, Mapping):
                d[k] = u[k]
            else:
                d = {k: u[k]}
        return d
    
  5. ==============================

    5.허용 된 솔루션과 동일한 솔루션이지만 더 명확한 변수 명명 인 docstring을 사용하고 값으로 {}가 무시되지 않는 버그가 수정되었습니다.

    허용 된 솔루션과 동일한 솔루션이지만 더 명확한 변수 명명 인 docstring을 사용하고 값으로 {}가 무시되지 않는 버그가 수정되었습니다.

    import collections
    
    
    def deep_update(source, overrides):
        """Update a nested dictionary or similar mapping.
    
        Modify ``source`` in place.
        """
        for key, value in overrides.iteritems():
            if isinstance(value, collections.Mapping) and value:
                returned = deep_update(source.get(key, {}), value)
                source[key] = returned
            else:
                source[key] = overrides[key]
        return source
    

    다음은 몇 가지 테스트 사례입니다.

    def test_deep_update():
        source = {'hello1': 1}
        overrides = {'hello2': 2}
        deep_update(source, overrides)
        assert source == {'hello1': 1, 'hello2': 2}
    
        source = {'hello': 'to_override'}
        overrides = {'hello': 'over'}
        deep_update(source, overrides)
        assert source == {'hello': 'over'}
    
        source = {'hello': {'value': 'to_override', 'no_change': 1}}
        overrides = {'hello': {'value': 'over'}}
        deep_update(source, overrides)
        assert source == {'hello': {'value': 'over', 'no_change': 1}}
    
        source = {'hello': {'value': 'to_override', 'no_change': 1}}
        overrides = {'hello': {'value': {}}}
        deep_update(source, overrides)
        assert source == {'hello': {'value': {}, 'no_change': 1}}
    
        source = {'hello': {'value': {}, 'no_change': 1}}
        overrides = {'hello': {'value': 2}}
        deep_update(source, overrides)
        assert source == {'hello': {'value': 2, 'no_change': 1}}
    

    이 함수는 charlatan 패키지의 charlatan.utils에서 사용할 수 있습니다.

  6. ==============================

    6.아무도 그것을 필요로하는 경우에 대비하여 재귀 사전 병합의 불변 버전이 있습니다.

    아무도 그것을 필요로하는 경우에 대비하여 재귀 사전 병합의 불변 버전이 있습니다.

    @Alex Martelli의 답을 바탕으로합니다.

    Python 2.x :

    import collections
    from copy import deepcopy
    
    
    def merge(dict1, dict2):
        ''' Return a new dictionary by merging two dictionaries recursively. '''
    
        result = deepcopy(dict1)
    
        for key, value in dict2.iteritems():
            if isinstance(value, collections.Mapping):
                result[key] = merge(result.get(key, {}), value)
            else:
                result[key] = deepcopy(dict2[key])
    
        return result
    

    Python 3.x :

    import collections
    from copy import deepcopy
    
    
    def merge(dict1, dict2):
        ''' Return a new dictionary by merging two dictionaries recursively. '''
    
        result = deepcopy(dict1)
    
        for key, value in dict2.items():
            if isinstance(value, collections.Mapping):
                result[key] = merge(result.get(key, {}), value)
            else:
                result[key] = deepcopy(dict2[key])
    
        return result
    
  7. ==============================

    7.이 두 가지 대답 중 어느 것도 저자는 사전에 저장된 객체를 업데이트하는 개념을 이해하지 못하는 것 같고 사전 항목을 반복하는 개념 (키가 아닌)을 이해하는 것처럼 보입니다. 그래서 무의미한 동사 사전 저장소와 검색을하지 않는 것을 작성해야했습니다. dicts는 다른 dicts 또는 단순한 유형을 저장하는 것으로 가정합니다.

    이 두 가지 대답 중 어느 것도 저자는 사전에 저장된 객체를 업데이트하는 개념을 이해하지 못하는 것 같고 사전 항목을 반복하는 개념 (키가 아닌)을 이해하는 것처럼 보입니다. 그래서 무의미한 동사 사전 저장소와 검색을하지 않는 것을 작성해야했습니다. dicts는 다른 dicts 또는 단순한 유형을 저장하는 것으로 가정합니다.

    def update_nested_dict(d, other):
        for k, v in other.items():
            if isinstance(v, collections.Mapping):
                d_v = d.get(k)
                if isinstance(d_v, collections.Mapping):
                    update_nested_dict(d_v, v)
                else:
                    d[k] = v.copy()
            else:
                d[k] = v
    

    또는 더 단순한 유형으로 작업하는 경우도 있습니다.

    def update_nested_dict(d, other):
        for k, v in other.items():
            d_v = d.get(k)
            if isinstance(v, collections.Mapping) and isinstance(d_v, collections.Mapping):
                update_nested_dict(d_v, v)
            else:
                d[k] = deepcopy(v) # or d[k] = v if you know what you're doing
    
  8. ==============================

    8.솔루션을 더욱 강력하게 만들기 위해 @Alex Martelli의 답변을 업데이트하여 코드의 버그를 수정하십시오.

    솔루션을 더욱 강력하게 만들기 위해 @Alex Martelli의 답변을 업데이트하여 코드의 버그를 수정하십시오.

    def update_dict(d, u):
        for k, v in u.items():
            if isinstance(v, collections.Mapping):
                default = v.copy()
                default.clear()
                r = update_dict(d.get(k, default), v)
                d[k] = r
            else:
                d[k] = v
        return d
    

    핵심은 재귀에서 동일한 유형을 만들고 싶기 때문에 v.copy (). clear ()를 사용하지만 {}을 사용하지 않습니다. 여기에있는 dict가 다른 종류의 default_factory를 가질 수있는 collections.defaultdict 유형 인 경우 특히 유용합니다.

    또한 u.iteritems ()가 Python3에서 u.items ()로 변경되었습니다.

  9. ==============================

    9.@Alex Martelli가 제안한 해결책을 사용했지만 실패했습니다.

    @Alex Martelli가 제안한 해결책을 사용했지만 실패했습니다.

    TypeError 'bool'객체가 항목 할당을 지원하지 않습니다.

    두 사전이 어느 정도 수준에서 데이터 유형이 다른 경우.

    사전 d의 요소가 스칼라 (예 : Bool) 인 반면, 같은 레벨에서 사전 u의 요소는 여전히 사전이지만 사전 할당은 True (k)와 같이 스칼라에 사전 할당이 불가능하므로 사전 할당이 실패합니다.

    하나의 조건을 추가하면 다음과 같이 수정됩니다.

    from collections import Mapping
    
    def update_deep(d, u):
        for k, v in u.items():
            # this condition handles the problem
            if not isinstance(d, Mapping):
                d = u
            elif isinstance(v, Mapping):
                r = update_deep(d.get(k, {}), v)
                d[k] = r
            else:
                d[k] = u[k]
    
        return d
    
  10. ==============================

    10.iteritems-Attribute가없는 나 같은 비표준 사전에 비틀 거릴 수 있습니다. 이 경우이 유형의 사전을 표준 사전으로 쉽게 해석 할 수 있습니다. 예 :

    iteritems-Attribute가없는 나 같은 비표준 사전에 비틀 거릴 수 있습니다. 이 경우이 유형의 사전을 표준 사전으로 쉽게 해석 할 수 있습니다. 예 :

    import collections
    def update(orig_dict, new_dict):
        for key, val in dict(new_dict).iteritems():
            if isinstance(val, collections.Mapping):
                tmp = update(orig_dict.get(key, { }), val)
                orig_dict[key] = tmp
            elif isinstance(val, list):
                orig_dict[key] = (orig_dict[key] + val)
            else:
                orig_dict[key] = new_dict[key]
        return orig_dict
    
    import multiprocessing
    d=multiprocessing.Manager().dict({'sample':'data'})
    u={'other': 1234}
    
    x=update(d, u)
    x.items()
    
  11. ==============================

    11.이 질문은 오래되었지만 "깊은 병합"솔루션을 검색 할 때 여기에 착륙했습니다. 위의 대답은 다음에 영감을주었습니다. 내가 테스트 한 모든 버전에 버그가 있었기 때문에 나는 내 자신의 글을 쓰게되었다. 놓친 중요한 점은 두 입력 dict의 임의의 깊이에서 어떤 키 k에 대해 d [k] 또는 u [k]가 dict가 아닌 결정 트리가 잘못되었음을 나타냅니다.

    이 질문은 오래되었지만 "깊은 병합"솔루션을 검색 할 때 여기에 착륙했습니다. 위의 대답은 다음에 영감을주었습니다. 내가 테스트 한 모든 버전에 버그가 있었기 때문에 나는 내 자신의 글을 쓰게되었다. 놓친 중요한 점은 두 입력 dict의 임의의 깊이에서 어떤 키 k에 대해 d [k] 또는 u [k]가 dict가 아닌 결정 트리가 잘못되었음을 나타냅니다.

    또한이 솔루션은 dict.update ()가 작동하는 방식과 더 대칭 인 재귀를 필요로하지 않으며 None을 반환합니다.

    import collections
    def deep_merge(d, u):
       """Do a deep merge of one dict into another.
    
       This will update d with values in u, but will not delete keys in d
       not found in u at some arbitrary depth of d. That is, u is deeply
       merged into d.
    
       Args -
         d, u: dicts
    
       Note: this is destructive to d, but not u.
    
       Returns: None
       """
       stack = [(d,u)]
       while stack:
          d,u = stack.pop(0)
          for k,v in u.items():
             if not isinstance(v, collections.Mapping):
                # u[k] is not a dict, nothing to merge, so just set it,
                # regardless if d[k] *was* a dict
                d[k] = v
             else:
                # note: u[k] is a dict
    
                # get d[k], defaulting to a dict, if it doesn't previously
                # exist
                dv = d.setdefault(k, {})
    
                if not isinstance(dv, collections.Mapping):
                   # d[k] is not a dict, so just set it to u[k],
                   # overriding whatever it was
                   d[k] = v
                else:
                   # both d[k] and u[k] are dicts, push them on the stack
                   # to merge
                   stack.append((dv, v))
    
  12. ==============================

    12.그건 약간 편이지만 중첩 된 사전이 정말로 필요한가요? 문제에 따라 때로는 평면 사전만으로도 충분할 수 있습니다.

    그건 약간 편이지만 중첩 된 사전이 정말로 필요한가요? 문제에 따라 때로는 평면 사전만으로도 충분할 수 있습니다.

    >>> dict1 = {('level1','level2','levelA'): 0}
    >>> dict1['level1','level2','levelB'] = 1
    >>> update = {('level1','level2','levelB'): 10}
    >>> dict1.update(update)
    >>> print dict1
    {('level1', 'level2', 'levelB'): 10, ('level1', 'level2', 'levelA'): 0}
    
  13. from https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth by cc-by-sa and MIT license