단일 표현식으로 두 개의 사전을 병합하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/38987

  •  09-06-2019
  •  | 
  •  

문제

두 개의 Python 사전이 있고 병합된 이 두 사전을 반환하는 단일 표현식을 작성하고 싶습니다.그만큼 update() 메서드가 dict를 내부에서 수정하는 대신 결과를 반환한다면 나에게 필요한 것일 것입니다.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

최종 병합된 사전을 어떻게 얻을 수 있나요? z, 아니다 x?

(더 명확하게 말하면, 마지막 사람이 승리하는 갈등 처리 방법은 다음과 같습니다. dict.update() 내가 찾고 있는 것도 바로 그것이다.)

도움이 되었습니까?

해결책

두 개의 Python 사전을 단일 표현식으로 병합하려면 어떻게 해야 합니까?

사전의 경우 x 그리고 y, z 의 값으로 얕게 병합된 사전이 됩니다. y 그것들을 대체하다 x.

  • Python 3.5 이상:

    z = {**x, **y}
    
  • Python 2(또는 3.4 이하)에서는 다음 함수를 작성합니다.

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    

    그리고 지금:

    z = merge_two_dicts(x, y)
    

설명

두 개의 사전이 있고 원래 사전을 변경하지 않고 이를 새 사전으로 병합하고 싶다고 가정해 보겠습니다.

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

원하는 결과는 새 사전(z) 값이 병합되고 두 번째 dict의 값이 첫 번째 값을 덮어씁니다.

>>> z
{'a': 1, 'b': 3, 'c': 4}

이에 대한 새로운 구문은 다음에서 제안되었습니다. PEP 448 그리고 Python 3.5부터 사용 가능, 이다

z = {**x, **y}

그리고 그것은 실제로 하나의 표현입니다.

리터럴 표기법으로도 병합할 수 있습니다.

z = {**x, 'foo': 1, 'bar': 2, **y}

그리고 지금:

>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}

이제 다음에서 구현된 것으로 표시됩니다. 3.5, PEP 478 출시 일정, 그리고 지금은 Python 3.5의 새로운 기능 문서.

그러나 많은 조직이 여전히 Python 2를 사용하고 있으므로 이전 버전과 호환되는 방식으로 이 작업을 수행하는 것이 좋습니다.Python 2 및 Python 3.0-3.4에서 사용할 수 있는 고전적인 Python 방식은 이 작업을 2단계 프로세스로 수행하는 것입니다.

z = x.copy()
z.update(y) # which returns None since it mutates z

두 가지 접근 방식 모두에서 y 두 번째가 되며 해당 값이 대체됩니다. x의 가치, 따라서 'b' 가리킬 것이다 3 우리의 최종 결과에서.

아직 Python 3.5에는 없지만 단일 표현식

아직 Python 3.5를 사용하지 않거나 이전 버전과 호환되는 코드를 작성해야 하고 이를 단일 표현식, 가장 성능이 좋지만 올바른 접근 방식은 함수에 넣는 것입니다.

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

그런 다음 단일 표현식이 있습니다.

z = merge_two_dicts(x, y)

0부터 매우 큰 수까지 정의되지 않은 수의 사전을 병합하는 함수를 만들 수도 있습니다.

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

이 함수는 모든 사전에 대해 Python 2 및 3에서 작동합니다.예를 들어주어진 dicts a 에게 g:

z = merge_dicts(a, b, c, d, e, f, g) 

및 키 값 쌍 g dict보다 우선합니다 a 에게 f, 등등.

다른 답변에 대한 비판

이전에 허용된 답변에 표시된 내용을 사용하지 마세요.

z = dict(x.items() + y.items())

Python 2에서는 각 사전에 대해 메모리에 두 개의 목록을 만들고 처음 두 개를 합친 길이와 동일한 길이로 메모리에 세 번째 목록을 만든 다음 세 목록을 모두 삭제하여 사전을 만듭니다. Python 3에서는 실패합니다. 두 개를 추가하기 때문에 dict_items 두 개의 목록이 아닌 함께 개체 -

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

명시적으로 목록으로 생성해야 합니다. z = dict(list(x.items()) + list(y.items())).이는 자원과 계산 능력의 낭비입니다.

마찬가지로, 결합을 취하면 items() 파이썬 3(viewitems() Python 2.7에서는 값이 해시할 수 없는 객체(예: 목록)인 경우에도 실패합니다.값이 해시 가능하더라도 세트는 의미상 순서가 없기 때문에 우선순위와 관련하여 동작이 정의되지 않습니다.그러니 이렇게 하지 마세요:

>>> c = dict(a.items() | b.items())

이 예에서는 값을 해시할 수 없을 때 어떤 일이 발생하는지 보여줍니다.

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

다음은 y가 우선순위를 가져야 하지만 대신 임의의 집합 순서로 인해 x의 값이 유지되는 예입니다.

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

사용하지 말아야 할 또 다른 해킹:

z = dict(x, **y)

이는 dict 생성자이며 매우 빠르고 메모리 효율적입니다(2단계 프로세스보다 약간 더 높음). 그러나 여기서 무슨 일이 일어나고 있는지 정확히 알지 않는 한(즉, 두 번째 dict가 dict 생성자에 키워드 인수로 전달됩니다) 읽기 어렵고, 의도한 사용법이 아니므로 Pythonic이 아닙니다.

다음은 사용법의 예입니다. django에서 수정됨.

Dicts는 해시 가능한 키를 사용하도록 고안되었습니다(예:Frozenset 또는 튜플), 그러나 키가 문자열이 아닌 경우 Python 3에서는 이 메서드가 실패합니다.

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

로부터 메일링 리스트, 언어 창시자인 Guido van Rossum은 다음과 같이 썼습니다.

나는 결국 ** 메커니즘의 남용이기 때문에 dict ({}, ** {1 : 3}) 불법을 선언하는 것은 괜찮습니다.

그리고

분명히 Dict (x, ** y)는 "x.update (y)를 호출하고 x"의 "Cool Hack"으로 돌아가고 있습니다.개인적으로 나는 시원한 것보다 더 멸시 할 수 있다고 생각합니다.

그것은 나의 이해이다(그리고 언어의 창시자) 의도된 용도는 다음과 같습니다. dict(**y) 가독성을 위해 dicts를 생성하는 것입니다. 예:

dict(a=1, b=10, c=11)

대신에

{'a': 1, 'b': 10, 'c': 11}

댓글에 대한 응답

귀도의 말에도 불구하고, dict(x, **y) btw.dict 사양과 일치합니다.Python 2와 3 모두에서 작동합니다.이것이 문자열 키에 대해서만 작동한다는 사실은 키워드 매개변수가 작동하는 방식의 직접적인 결과이지 dict의 단점이 아닙니다.여기서 ** 연산자를 사용하는 것도 메커니즘을 남용하는 것이 아닙니다. 실제로 **는 사전을 키워드로 전달하도록 정확하게 설계되었습니다.

다시 말하지만 키가 문자열이 아닌 경우 3에서는 작동하지 않습니다.암시적 호출 계약은 네임스페이스가 일반 사전을 사용하는 반면 사용자는 문자열인 키워드 인수만 전달해야 한다는 것입니다.다른 모든 호출 가능 항목은 이를 시행했습니다. dict Python 2에서는 이러한 일관성이 깨졌습니다.

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

이러한 불일치는 Python의 다른 구현(Pypy, Jython, IronPython)에서는 나빴습니다.따라서 이 사용법은 획기적인 변경이 될 수 있으므로 Python 3에서 수정되었습니다.

한 가지 버전의 언어에서만 작동하거나 특정한 임의의 제약 조건이 주어졌을 때에만 작동하는 코드를 의도적으로 작성하는 것은 악의적인 무능력입니다.

댓글 더보기:

dict(x.items() + y.items()) 여전히 Python 2에서 가장 읽기 쉬운 솔루션입니다.가독성이 중요합니다.

나의 응답: merge_two_dicts(x, y) 실제로 가독성에 관심이 있다면 실제로는 훨씬 더 명확해 보입니다.그리고 Python 2가 점점 더 이상 사용되지 않으므로 앞으로 호환되지 않습니다.

{**x, **y} 중첩된 사전을 처리하지 않는 것 같습니다.중첩된 키의 내용은 병합되지 않고 단순히 덮어쓰여집니다. [...] 나는 결국 재귀적으로 병합되지 않는 이러한 답변으로 인해 화상을 입었고 아무도 언급하지 않은 것에 놀랐습니다."병합"이라는 단어에 대한 내 해석에서 이러한 답변은 병합이 아닌 "한 사전을 다른 사전으로 업데이트"하는 것을 설명합니다.

예.나는 당신에게 다음을 요구하는 질문을 다시 언급해야 합니다. 얕은 병합 단일 표현식에서 첫 번째 값이 두 번째 값으로 덮어쓰이는 사전.

두 개의 사전 사전을 가정하면 하나의 함수로 이를 재귀적으로 병합할 수 있지만 어느 소스에서든 사전을 수정하지 않도록 주의해야 하며 이를 방지하는 가장 확실한 방법은 값을 할당할 때 복사본을 만드는 것입니다.키는 해시 가능해야 하고 일반적으로 불변이므로 복사하는 것은 의미가 없습니다.

from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

용법:

>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

다른 가치 유형에 대한 우발적 상황을 생각해내는 것은 이 질문의 범위를 훨씬 넘어서므로 다음 사항을 지적하겠습니다. "사전 병합"에 대한 정식 질문에 대한 내 대답.

성능은 떨어지지만 Ad-hoc은 정확함

이러한 접근 방식은 성능은 떨어지지만 올바른 동작을 제공합니다.그들은 될 것이다 훨씬 적은 보다 성능이 좋다 copy 그리고 update 또는 더 높은 수준의 추상화에서 각 키-값 쌍을 반복하기 때문에 새로운 압축 풀기이지만 하다 우선순위를 존중합니다(나중의 dict가 우선합니다).

dict comprehension 내에서 dict를 수동으로 연결할 수도 있습니다.

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

또는 Python 2.6(그리고 아마도 생성기 표현식이 도입된 2.4 초기)에서:

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain 올바른 순서로 키-값 쌍에 대해 반복자를 연결합니다.

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

성능 분석

올바르게 작동하는 것으로 알려진 사용법에 대해서만 성능 분석을 수행하겠습니다.

import timeit

다음은 Ubuntu 14.04에서 수행됩니다.

Python 2.7(시스템 Python):

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

Python 3.5(deadsnakes PPA):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

사전에 대한 자료

다른 팁

귀하의 경우 할 수 있는 일은 다음과 같습니다.

z = dict(x.items() + y.items())

이것은 당신이 원하는 대로 최종 dict를 넣을 것입니다. z, 키 값을 만듭니다. b 두 번째(y) 사전의 값:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

Python 3을 사용하면 조금 더 복잡해집니다.만들다 z:

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

대안:

z = x.copy()
z.update(y)

좀 더 간결한 또 다른 옵션은 다음과 같습니다.

z = dict(x, **y)

메모:이것은 대중적인 답변이 되었지만, 다음과 같은 점을 지적하는 것이 중요합니다. y 문자열이 아닌 키가 있는 경우 이것이 작동한다는 사실은 CPython 구현 세부 사항을 남용하는 것이며 Python 3, PyPy, IronPython 또는 Jython에서는 작동하지 않습니다.또한, 귀도는 팬이 아니다.따라서 앞으로 호환되거나 구현 간 이식 가능한 코드에는 이 기술을 권장할 수 없습니다. 이는 실제로 이 기술을 완전히 피해야 함을 의미합니다.

이것은 아마도 대중적인 대답은 아닐 것입니다. 그러나 당신은 이것을 하고 싶지 않을 것이 거의 확실합니다.병합된 사본을 원할 경우 사본(또는 딥카피, 원하는 내용에 따라) 업데이트합니다.두 줄의 코드는 .items() + .items()를 사용하여 한 줄로 생성하는 것보다 훨씬 더 읽기 쉽고 Python에 가깝습니다.명시적인 것이 암시적인 것보다 낫습니다.

또한 .items()(Python 3.0 이전)를 사용하면 dict의 항목을 포함하는 새 목록이 생성됩니다.사전이 크면 오버헤드가 상당히 많이 발생합니다(병합된 사전이 생성되자마자 버려지는 두 개의 큰 목록).update()는 두 번째 dict를 항목별로 실행할 수 있기 때문에 더 효율적으로 작동할 수 있습니다.

측면에서 시간:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO에서는 처음 두 개 사이의 약간의 속도 저하가 가독성 측면에서 그만한 가치가 있습니다.또한 사전 생성을 위한 키워드 인수는 Python 2.3에만 추가된 반면, copy() 및 update()는 이전 버전에서 작동합니다.

후속 답변에서 다음 두 가지 대안의 상대적 성능에 대해 질문하셨습니다.

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

내 컴퓨터에서는 적어도 (Python 2.5.2를 실행하는 상당히 일반적인 x86_64) 대안이 있습니다. z2 더 짧고 간단할 뿐만 아니라 훨씬 더 빠릅니다.이를 사용하여 직접 확인할 수 있습니다. timeit Python과 함께 제공되는 모듈입니다.

예시 1:20개의 연속된 정수를 자신에게 매핑하는 동일한 사전:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2 3.5배 정도의 차이로 승리합니다.다른 사전은 상당히 다른 결과를 산출하는 것처럼 보이지만, z2 항상 앞서 나가는 것 같아요.(일관되지 않은 결과가 나오는 경우 같은 테스트하고, 전달해 보세요 -r 기본값인 3보다 큰 숫자를 사용합니다.)

예시 2:252개의 짧은 문자열을 정수로 또는 그 반대로 매핑하는 비중첩 사전:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 약 10배 차이로 승리합니다.그것은 내 책에서 꽤 큰 승리입니다!

두 가지를 비교한 후 궁금했습니다. z1의 저조한 성능은 두 개의 항목 목록을 구성하는 데 따른 오버헤드로 인해 발생할 수 있으며, 이로 인해 이 변형이 더 잘 작동할 수 있을지 궁금해졌습니다.

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

몇 가지 빠른 테스트.

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

내가 그런 결론을 내리도록 이끌어줘 z3 보다 다소 빠르다 z1, 그러나 거의만큼 빠르지는 않습니다. z2.확실히 추가 타이핑을 할 가치가 없습니다.

이 토론에는 두 목록을 병합하는 "명백한" 방법과 이러한 대안의 성능 비교라는 중요한 내용이 여전히 빠져 있습니다.사용하여 update 방법.x 또는 y를 수정하지 않는 표현식과 동일한 기반을 유지하기 위해 다음과 같이 x를 내부 수정하는 대신 x의 복사본을 만들겠습니다.

z0 = dict(x)
z0.update(y)

일반적인 결과:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

다시 말해서, z0 그리고 z2 본질적으로 동일한 성능을 보이는 것 같습니다.이것이 우연일지도 모른다고 생각하시나요?나는 아니에요....

사실, 나는 순수한 Python 코드가 이보다 더 나은 기능을 수행하는 것은 불가능하다고 주장하고 싶습니다.그리고 C 확장 모듈에서 훨씬 더 나은 결과를 얻을 수 있다면 Python 사람들이 귀하의 코드(또는 접근 방식의 변형)를 Python 코어에 통합하는 데 관심이 있을 것이라고 생각합니다.파이썬은 dict 많은 곳에서;운영을 최적화하는 것은 큰 일입니다.

이것을 다음과 같이 쓸 수도 있습니다.

z0 = x.copy()
z0.update(y)

Tony가 그랬듯이, 그러나 (놀랍지도 않게) 표기법의 차이는 성능에 측정 가능한 영향을 미치지 않는 것으로 나타났습니다.당신에게 딱 맞는 것을 사용하십시오.물론, 그가 두 문 버전이 훨씬 이해하기 쉽다고 지적한 것은 전적으로 옳습니다.

나는 비슷한 것을 원했지만 중복 키의 값이 병합되는 방법을 지정하는 기능이 있어서 이것을 해킹했습니다(그러나 많이 테스트하지는 않았습니다).분명히 이것은 단일 표현식이 아니라 단일 함수 호출입니다.

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

Python 3에서는 다음을 사용할 수 있습니다. collections.ChainMap 여러 사전 또는 기타 매핑을 함께 그룹화하여 업데이트 가능한 단일 보기를 만듭니다.

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

재귀적으로/깊이 업데이트하여 딕셔너리

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

데모:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

출력:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

편집해 주셔서 감사합니다.

복사를 사용하지 않는 동안 내가 생각할 수 있는 가장 좋은 버전은 다음과 같습니다.

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

보다 빠르다 dict(x.items() + y.items()) 하지만 그만큼 빠르지는 않다 n = copy(a); n.update(b), 적어도 CPython에서는요.이 버전은 변경하면 Python 3에서도 작동합니다. iteritems() 에게 items(), 이는 2to3 도구에 의해 자동으로 수행됩니다.

개인적으로 나는 이 버전이 내가 원하는 것을 단일 기능 구문으로 상당히 잘 설명하기 때문에 가장 좋아합니다.유일한 사소한 문제는 y의 값이 x의 값보다 우선한다는 사실이 완전히 명확하지 않다는 것입니다. 그러나 나는 그것을 알아내는 것이 어렵지 않다고 생각합니다.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

두 사전('b')에 모두 키가 있는 항목의 경우, 어느 사전을 마지막에 넣어 출력에 표시되는지 제어할 수 있습니다.

Python 3.5(PEP 448)에서는 더 나은 구문 옵션을 허용합니다.

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

또는

final = {'a': 1, 'b': 1, **x, **y}

질문은 이미 여러 번 답변되었지만 문제에 대한이 간단한 솔루션은 아직 나열되지 않았습니다.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

위에서 언급한 z0 및 사악한 z2만큼 빠르지만 이해하고 변경하기 쉽습니다.

def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

이렇게 모호하고 모호한 답변 중에서 이 빛나는 예는 독재자가 평생 승인한 Python에서 사전을 병합하는 유일한 좋은 방법입니다. 귀도 반 로섬 그 자신!다른 사람이 이 중 절반을 제안했지만 함수에 넣지는 않았습니다.

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

다음을 제공합니다:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

람다가 나쁘다고 생각한다면 더 이상 읽지 마십시오.요청에 따라 다음 표현식 하나로 빠르고 메모리 효율적인 솔루션을 작성할 수 있습니다.

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

위에서 제안한 것처럼 두 줄을 사용하거나 함수를 작성하는 것이 아마도 더 좋은 방법일 것입니다.

파이썬처럼 행동하세요.사용 이해력:

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

Python3에서는 items 방법 더 이상 목록을 반환하지 않습니다., 그러나 오히려 보다, 이는 세트처럼 작동합니다.이 경우에는 다음과 연결한 이후 집합 합집합을 가져와야 합니다. + 작동하지 않습니다:

dict(x.items() | y.items())

버전 2.7의 python3과 유사한 동작의 경우 viewitems 방법이 대신 작동해야합니다 items:

dict(x.viewitems() | y.viewitems())

(제목에서 볼 수 있듯이) 연결보다는 집합 합집합 연산으로 생각하는 것이 더 자연스러워 보이기 때문에 어쨌든 나는 이 표기법을 선호합니다.

편집하다:

Python 3에 대한 몇 가지 추가 사항입니다.먼저, dict(x, **y) 키가 없으면 Python 3에서는 트릭이 작동하지 않습니다. y 문자열입니다.

또한, Raymond Hettinger의 체인맵 답변 임의의 수의 사전을 인수로 사용할 수 있기 때문에 매우 우아합니다. 문서에서 각 조회에 대한 모든 사전 목록을 순차적으로 살펴보는 것처럼 보입니다.

조회는 키를 찾을 때까지 기본 매핑을 연속적으로 검색합니다.

애플리케이션에 조회가 많으면 속도가 느려질 수 있습니다.

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

따라서 조회 속도가 약 10배 정도 느려집니다.저는 체인맵(Chainmap)의 팬이지만 조회가 많을 경우 실용성이 떨어지는 것 같습니다.

단일 표현 솔루션으로 이어지는 학대 마태의 대답:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

한 가지 표현을 원한다고 해서 욕했어요 lambda 이름을 바인딩하고 튜플을 사용하여 람다의 단일 표현식 제한을 재정의합니다.마음껏 울어보세요.

물론 복사에 신경 쓰지 않는다면 이 작업을 수행할 수도 있습니다.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

순서를 유지하는 itertools를 사용하는 간단한 솔루션(후자의 사전이 우선)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

사용법은 다음과 같습니다.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}

두 개의 사전

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

N 사전

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum 성능이 좋지 않습니다.보다 https://mathieularose.com/how-not-to-Flatten-a-list-of-lists-in-python/

이에 대한 답변은 좋았음에도 불구하고 얕은 여기에 정의된 메서드 중 실제로 깊은 사전 병합을 수행하는 메서드는 없습니다.

예는 다음과 같습니다:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

다음과 같은 결과가 나올 것으로 예상됩니다.

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

대신에 우리는 이것을 얻습니다:

{'two': True, 'one': {'extra': False}}

'one' 항목이 실제로 병합된 경우 사전 내 항목으로 'length_2' 및 'extra'가 있어야 합니다.

체인을 사용해도 작동하지 않습니다.

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

결과:

{'two': True, 'one': {'extra': False}}

rcwesick이 제공한 deep merge도 동일한 결과를 생성합니다.

예, 샘플 사전을 병합하면 작동하지만 그 중 어느 것도 병합할 수 있는 일반적인 메커니즘은 아닙니다.나중에 실제 병합을 수행하는 메서드를 작성하면 이를 업데이트하겠습니다.

여기와 다른 곳에서 아이디어를 끌어내면서 다음과 같은 기능을 이해했습니다.

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

사용법(Python 3에서 테스트됨):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

대신 람다를 사용할 수 있습니다.

현재까지 나열된 솔루션에서 내가 겪고 있는 문제는 병합된 사전에서 키 "b"의 값이 10이지만 내 생각으로는 12여야 한다는 것입니다.그런 관점에서 나는 다음을 제시한다.

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

결과:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}

이는 단일 dict comprehension으로 수행할 수 있습니다.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

내 생각에는 추가 기능이 필요하지 않기 때문에 '단일 표현식' 부분에 대한 가장 좋은 대답은 짧습니다.

from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

그러면 문제가 해결될 것입니다.

(Python2.7*에만 해당;Python3*에 대한 더 간단한 솔루션이 있습니다.)

표준 라이브러리 모듈을 가져오는 것을 반대하지 않는다면 다음을 수행할 수 있습니다.

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(그만큼 or a 조금 lambda 때문에 필요하다 dict.update 항상 반환 None 성공 시.)

너무 어리석은 일이야 .update 아무것도 반환하지 않습니다.
문제를 해결하기 위해 간단한 도우미 함수를 사용합니다.

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

예:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

사전 이해를 사용하면 다음을 수행할 수 있습니다.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

준다

>>> dc
{'a': 1, 'c': 11, 'b': 10}

구문에 유의하세요. if else 이해하다

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

나는 이것이 질문의 세부 사항("한 줄")에 실제로 맞지 않는다는 것을 알고 있지만, 없음 위의 답변 중 많은 답변이 성능 문제를 다루면서 이 방향으로 진행되었으므로 내 생각을 기여해야 한다고 느꼈습니다.

사용 사례에 따라 주어진 입력 사전의 "실제" 병합 사전을 생성할 필요가 없을 수도 있습니다.ㅏ 보다 이는 많은 경우에 충분할 수 있습니다.이자형.행동하는 물체 좋다 병합된 사전은 완전히 계산하지 않을 것입니다.말하자면 병합된 사전의 게으른 버전입니다.

Python에서는 이 작업이 매우 간단하며 내 게시물 끝에 표시된 코드를 사용하여 수행할 수 있습니다.이 경우 원래 질문에 대한 대답은 다음과 같습니다.

z = MergeDict(x, y)

이 새 개체를 사용하면 병합된 사전처럼 동작하지만 원본 사전은 그대로 유지하면서 생성 시간과 메모리 공간이 일정하게 유지됩니다.그것을 만드는 것은 제안된 다른 솔루션보다 훨씬 저렴합니다.

물론 결과를 많이 사용하면 어느 시점에서는 실제 병합된 사전을 만드는 것이 더 빠른 솔루션이 될 수 있는 한계에 도달하게 됩니다.내가 말했듯이 사용 사례에 따라 다릅니다.

만약 당신이 실제 병합을 선호한다고 느낀 적이 있다면 dict, 그런 다음 전화 dict(z) 그것을 생산할 것입니다(물론 다른 솔루션보다 비용이 많이 들기 때문에 언급할 가치가 있습니다).

이 클래스를 사용하여 일종의 쓰기 중 복사 사전을 만들 수도 있습니다.

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

다음은 간단한 코드입니다. MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top