두 개의 사전을 하나의 표현식으로 어떻게 병합합니까 (사전 조합)?


질문

 

나는 두 개의 파이썬 사전을 가지고 있으며,이 두 사전을 반환하는 단일 표현식을 작성하고 병합 (즉, 노동 조합을 복용합니다).Update () 메서드는 사전 내에서 사전을 수정하는 대신 결과를 반환하면 필요한 것입니다.

>>> 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에서 최종 병합 사전을 어떻게 얻을 수 있습니까?

(여분의 분명한 것, 지속적인 일의 분쟁 취급 ()은 내가 찾고있는 것입니다.)


답변

 

두 개의 파이썬 사전을 하나의 표현식으로 어떻게 병합 할 수 있습니까?

Dictionaries X 및 Y의 경우 Z는 your에서 값을 X에서 대체하는 값이있는 얕은 병합 된 사전이됩니다.

파이썬 3.9.0 이상 (릴리스 1720 년 10 월 2020 일 릴리스) : 여기에서 논의 된 PEP-584는 구현되었으며 가장 간단한 방법을 제공합니다. z = x |Y # 참고 : 3.9+ only. 파이썬 3.5 이상에서 : z = {** x, ** y} 파이썬 2에서 (또는 3.4 이하) 함수 쓰기 : def merge_two_dicts (x, y) : z = x.copy () # 키와 값을 시작으로 시작합니다. z.Update (y) # z의 키와 값을 수정합니다. 반환 Z. 그리고 지금: z = merge_two_dicts (x, y)

설명

2 개의 사전이 있고 원래 사전을 변경하지 않고 새 사전으로 병합하려고합니다.

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

원하는 결과는 값 병합 값을 사용하여 새 사전 (z)을 가져오고 두 번째 사전의 값은 첫 번째 사전의 값을 덮어 쓰는 것입니다.

>>> 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 문서의 새로운 기능으로가는 것으로 나타났습니다.

그러나 많은 조직이 아직 파이썬 2에 있기 때문에이 작업을 후방 호환 방식으로 할 수 있습니다.Python 2 및 Python 3.0-3.4에서 사용할 수있는 고전적으로 Pyhonic Way는 2 단계 프로세스로 수행하는 것입니다.

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

두 접근 방식에서 Y가 두 번째로 올 것이고 그 값은 X의 값을 대체 할 것이므로 B는 최종 결과에서 3을 가리 킵니다.

아직 파이썬 3.5에 없지만 단일 표현을 원한다.

아직 파이썬 3.5에 있지 않거나 역 호환 코드를 작성해야하며, 단일 표현식 에서이 기능을 원하는 경우 올바른 접근 방식이 기능을 수행하는 것입니다.

def merge_two_dicts(x, y):
    """Given two dictionaries, 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 dictionaries, shallow copy and merge into a new dict,
    precedence goes to key-value pairs in latter dictionaries.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

이 기능은 모든 사전에 대해 Python 2와 3에서 작동합니다.예를 들어,사전 A에 G :

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

G의 키 값 쌍은 사전 A에서 F로 우선합니다.

다른 답변의 비평

이전에 받아 들여지는 대답에서 보는 것을 사용하지 마십시오.

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

파이썬 2에서 각 DICT에 대해 메모리에 두 개의 목록을 만듭니다. 길이가있는 메모리에 세 번째 목록을 만듭니다. 첫 번째 두 개의 길이와 동일한 길이가있는 다음 세 개의 목록을 모두 삭제하여 DICT를 만듭니다.파이썬 3에서는 두 개의 dict_items 객체를 함께 추가하고 두 개의 목록이 아닌 두 개의 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 ()) + 목록 (y.items ())).이것은 자원 및 계산력 낭비입니다.

마찬가지로 Python 3 (Python 2.7의 ViewItems ())에서 항목 ()을 복용하면 값이 unchableable 객체 (예 : 예 : 목록) 일 때도 실패합니다.가치가 해시 가능하더라도 세트가 의미가 없기 때문에 비헤이비어는 우선 순위에 따라 정의되지 않습니다.그렇게하지 마십시오 :

>>> 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에 키워드 인수로 전달되고 있습니다.생성자), 읽는 것이 어렵고, 그것은 의도 된 사용법이 아니므로 pythonic이 아닙니다.

다음은 Django에서 수정되는 사용법의 예입니다.

사전은 해시 가능한 열쇠 (예 : 얼어 붙음 또는 튜플)를 취할 것으로 예상되지만 키가 문자열이 아닌 경우 파이썬 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)는 "Cool Hack"으로 "Cool Hack" x.update (y) 및 x "를 리턴하십시오. 개인적으로, 나는 그것이 시원한.

DICT (** y)에 대한 의도 된 사용법이 가독성 목적을위한 사전을 만드는 것입니다.

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

대신에

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

의견에 대한 응답

Guido가 말하는 것에도 불구하고 Dict (x, ** y)는 BTW의 DICT 사양과 일치합니다.파이썬 2와 3을 모두 일하고 있습니다.이 작업은 문자열 키에만 작동하는 사실은 키워드 매개 변수가 작동하는 방식과 짧은 딕이 아닌 방법의 직접적인 결과입니다.** 오퍼레이터를 사용하고 있습니다. 실제로 메커니즘을 남용해도 **는 사전을 키워드로 전달하기 위해 정확하게 설계되었습니다.

다시 키가 문자열이 아닌 경우에는 작동하지 않습니다.암시 적 호출 계약은 네임 스페이스가 일반 사전을 사용하는 것입니다. 사용자는 문자열 인 키워드 인수 만 전달해야합니다.다른 모든 통화 상자가 적용됩니다.Dict는 파이썬 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}

이 불일치는 파이썬 (Pypy, Jython, Ironpython)의 다른 구현을 주어졌습니다.따라서이 사용법이 깨는 변화가 될 수 있으므로 파이썬 3에서 고정되었습니다.

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

더 많은 의견 :

dict (x.items () + y.items ())는 여전히 파이썬을위한 가장 읽을 수있는 솔루션입니다. 2. 가독성 카운트.

내 응답 : merge_two_dicts (x, y)는 실제로 가독성에 대해 실제로 염려하는 경우 실제로 나에게 훨씬 더 명확 해 보입니다.파이썬 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: {}}}

다른 가치 유형에 대한 우발 이이 질문의 범위를 훨씬 뛰어 넘는 일이므로 "사전 병합의 사전"에 대한 정식적 인 질문에 대한 대답을 지적 할 것입니다.

덜 공연자이지만 올바른 광고 -

이러한 접근법은 공연력이 적지만 올바른 행동을 제공합니다. 복사 및 업데이트보다 훨씬 적은 성과 또는 새로운 추상화에서 각 키 가치 쌍을 반복하기 때문에 훨씬 흡사하지만 우선 순위의 순서를 존중합니다 (후자 사전 우선)

Dict Permementes 내부에서 사전을 수동으로 체인 할 수도 있습니다.

{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()) # iteritems in Python 2

IterTools.chain은 순서가 올바른 순서로 키 값 쌍을 통해 반복자를 연결합니다.

from itertools import chain
z = dict(chain(x.items(), y.items())) # iteritems in Python 2

성능 분석

나는 단지 올바르게 행동하는 것으로 알려진 사용법의 성능 분석을 할 것입니다.(자체 포함되어 있으므로 자신을 복사하여 붙여 넣을 수 있습니다.)

from timeit import repeat
from itertools import chain

x = dict.fromkeys('abcdefg')
y = dict.fromkeys('efghijk')

def merge_two_dicts(x, y):
    z = x.copy()
    z.update(y)
    return z

min(repeat(lambda: {**x, **y}))
min(repeat(lambda: merge_two_dicts(x, y)))
min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
min(repeat(lambda: dict(chain(x.items(), y.items()))))
min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))

파이썬 3.8.1, 닉스 :

>>> min(repeat(lambda: {**x, **y}))
1.0804965235292912
>>> min(repeat(lambda: merge_two_dicts(x, y)))
1.636518670246005
>>> min(repeat(lambda: {k: v for d in (x, y) for k, v in d.items()}))
3.1779992282390594
>>> min(repeat(lambda: dict(chain(x.items(), y.items()))))
2.740647904574871
>>> min(repeat(lambda: dict(item for d in (x, y) for item in d.items())))
4.266070580109954
$ uname -a
Linux nixos 4.19.113 #1-NixOS SMP Wed Mar 25 07:06:15 UTC 2020 x86_64 GNU/Linux

사전에 대한 자원

3.6을 위해 업데이트 된 Python의 사전 구현에 대한 내 설명. 사전에 새 키를 추가하는 방법에 대한 답변 두 개의 목록을 사전에 매핑합니다 사전에 대한 공식 파이썬 문서 도구 Dictionary조차도 Mightier - Pycon 2017의 Brandon Rhodes의 이야기 현대 파이썬 사전, 위대한 아이디어의 합류점 - Raymond Hettinger가 Pycon 2017의 이야기



답변

귀하의 경우에, 당신이 할 수있는 일은 다음과 같습니다.

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

이것은 원하는대로 최종 DICT를 Z에 넣고 키 B에 대한 값을 두 번째 (y) DICT의 가치에 적절하게 무시하도록하십시오.

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

파이썬 2를 사용하는 경우 list () 호출을 제거 할 수도 있습니다.z를 만들려면 :

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

Python 버전 3.9.0A4 이상을 사용하는 경우 직접 사용할 수 있습니다.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)
{'a': 1, 'c': 11, 'b': 10}


답변

대안:

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


답변

또 다른,보다 간결한 옵션 :

z = dict(x, **y)

참고 : 이것은 인기있는 답변이되었지만 Y가 아닌 문자열 키가있는 경우이 모든 것이 CPYTHON 구현 세부 사항의 남용이 있으며 파이썬 3에서는 작동하지 않는다는 사실을 지적하는 것이 중요합니다.또는 Pypy, Ironpython 또는 Jython에서.또한 Guido는 팬이 아닙니다.따라서 앞으로 호환되거나 교차 구현 휴대용 코드를 위해이 기술을 추천 할 수 없으므로 실제로는 완전히 피해야합니다.



답변

이것은 아마도 인기있는 답변이 아닐 것입니다. 그러나 거의 확실히 이것을하고 싶지 않습니다.병합의 복사본을 원한다면 복사 (또는 deepCopy, 원하는 항목에 따라 DeepCopy)를 사용하고 업데이트하십시오.두 줄의 코드는 훨씬 더 읽을 수 있습니다.명시 적으로는 암시 적보다 낫습니다.

또한 .Items () (Prey Python 3.0)를 사용하면 Dict에서 항목이 포함 된 새 목록을 만드는 것이 좋습니다.사전이 크게 넓은 경우, 그것은 꽤 많은 오버 헤드 (병합 된 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에만 추가되었습니다. 반면 복사 () 및 UPDATE ()는 이전 버전에서 작동합니다.



답변

후속 대답에서는이 두 가지 대안의 상대적 성능에 대해 물었습니다.

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

내 기계에서 적어도 (평범한 x86_64 실행 파이썬 2.5.2를 실행), 대안 z2는 짧고 단순할뿐만 아니라 훨씬 빠릅니다.파이썬과 함께 제공되는 시간대 모듈을 사용하여이를 확인할 수 있습니다.

예제 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는 항상 앞으로 나오는 것처럼 보입니다.동일한 테스트에 대해 일치하지 않는 결과를 얻는 경우 기본 3보다 큰 숫자로 -R을 전달하십시오.)

예제 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만큼 빨리 z3이 아니라고 결론 지어 라.모든 여분의 타이핑의 가치가 없습니다.

이 토론은 여전히 중요한 것으로 중요합니다.이 대안을 "명백한"방법으로 성능 비교 인 두 명의 목록을 "업데이트 방법을 사용하십시오.표현식으로 동등한 발자국에 물건을 유지하려고하는 것은 X 또는 Y를 수정하지 않으며 다음과 같이 IT-Place를 수정하는 대신 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는 본질적으로 동일한 성능을 갖는 것으로 보인다.이것이 우연이 될 것이라고 생각하니?나는 ....

사실, 순수한 파이썬 코드 가이보다 나은 일을하는 것이 불가능하다는 것을 주장하는 것처럼 보이는 것입니다.그리고 C 확장 모듈에서 훨씬 더 잘 할 수 있다면 파이썬 사람들이 파이썬 코어에 코드 (또는 접근 방식의 변형)를 통합하는 데 중요 할 수 있습니다.파이썬은 많은 곳에서 dict를 사용합니다.그 작업을 최적화하는 것은 큰 거래입니다.

당신은 또한 이것을 쓸 수 있습니다

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

토니가 그렇듯이 (놀랍지 않게) 표기법의 차이는 성과에 대한 측정 가능한 효과가 없게됩니다.당신에게 옳은 것을 사용하십시오.물론, 그는 2-statement 버전이 훨씬 더 쉽게 이해하기 쉽다는 것을 지적하기 위해 절대적으로 정확합니다.

출처:https://stackoverflow.com/questions/38987/how-do-i-merge-two-dictionaries-in-a-single-expression-take-union-of-dictionari