etc./StackOverFlow

두 개의 사전을 단일 표현식으로 병합하려면(사전 통합 사용) 어떻게 하나요?

청렴결백한 만능 재주꾼 2021. 9. 23. 22:51
반응형

질문자 :Carl Meyer


두 개의 Python 사전이 있고 병합된(즉, 합집합 사용) 이 두 사전을 반환하는 단일 표현식을 작성하려고 합니다. 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}

x 아닌 z 에서 최종 병합 사전을 어떻게 얻을 수 있습니까?

dict.update() 의 마지막 1승 충돌 처리도 제가 찾고 있는 것입니다.)



답변자 : Aaron Hall


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

사전 들어 xy , z 의 값을 가진 얕게 사전 - 병합된다 y 에서 그 교체 x .

  • Python 3.9.0 이상(2020년 10월 17일 릴리스): 여기에 설명된 PEP-584 가 구현되었으며 가장 간단한 방법을 제공합니다.

     z = x | y # NOTE: 3.9+ ONLY
  • Python 3.5 이상:

     z = {**x, **y}
  • Python 2(또는 3.4 이하)에서 함수를 작성하십시오.

     def merge_two_dicts(x, y): z = x.copy() # start with keys and values of x z.update(y) # modifies z with keys and values of y return z

    그리고 지금:

     z = merge_two_dicts(x, y)

설명

두 개의 사전이 있고 원래 사전을 변경하지 않고 새 사전으로 병합하려는 경우:

 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의 새로운 기능 문서에 포함되었습니다.

그러나 많은 조직이 여전히 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 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())

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())) 와 같이 명시적으로 목록으로 만들어야 합니다. 이것은 자원과 계산 능력의 낭비입니다.

유사하게, 파이썬 3에서 items() viewitems() 또한 값이 해시할 수 없는 객체(예: 목록)인 경우 실패합니다. 값이 해시 가능하더라도 집합은 의미적으로 순서가 지정되지 않으므로 우선 순위와 관련하여 동작이 정의되지 않습니다. 따라서 다음과 같이 하지 마십시오.

 >>> 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 에서 수정되는 사용법의 예입니다.

사전은 해시 가능한 키(예: 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 반환"에 대한 "멋진 해킹"으로 돌아갑니다. 개인적으로 쿨하다기 보다는 징그럽다고 생각합니다.

그것은 나의 이해 (뿐만 아니라의 이해입니다 언어의 창조자 의 사용 목적 것을) dict(**y) 읽기 쉽도록 예에 대한 사전을 만들기위한 것입니다 :

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

대신에

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

댓글에 대한 응답

Guido가 말한 것에도 불구하고 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: {}}}

다른 값 유형에 대한 우연성을 생각해 내는 것은 이 질문의 범위를 훨씬 벗어나므로 "Dictionaries of dictionaries merge"에 대한 표준 질문에 대한 답변을 알려 드리겠습니다 .

성능은 떨어지지만 올바른 애드혹

이러한 접근 방식은 성능이 떨어지지만 올바른 동작을 제공합니다. 그들은 훨씬 성능이 좋은보다 것이다 copyupdate 그들이 높은 수준의 추상화에서 각 키 - 값 쌍을 반복하기 때문에 또는 새로운 포장을 해체하고 있지만 우선 순위를 (후자의 사전이 우선 순위가) 존중하지

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

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

Python 3.8.1, NixOS:

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

사전에 대한 리소스



답변자 : Thomas Vander Stichele


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

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

이렇게 하면 원하는 대로 최종 사전을 z b 의 값 y ) 사전의 값으로 적절하게 재정의되도록 합니다.

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

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


답변자 : Matthew Schinckel


대안:

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


답변자 : Carl Meyer


더 간결한 또 다른 옵션:

 z = dict(x, **y)

참고 y 문자열이 아닌 키가 있는 경우 이것이 전혀 작동한다는 사실은 CPython 구현 세부 사항을 남용하는 것이며 Python 3에서는 작동하지 않는다는 점을 지적하는 것이 중요합니다. , 또는 PyPy, IronPython 또는 Jython. 또한 귀도는 팬이 아닙니다 . 따라서 저는 이 기술을 앞으로 호환 가능하거나 교차 구현 이식 가능한 코드에 권장할 수 없습니다. 즉, 완전히 피해야 함을 의미합니다.



답변자 : Tony Meyer


이것은 아마도 대중적인 대답은 아닐 것이지만, 당신은 거의 확실히 이것을 하고 싶지 않을 것입니다. 병합된 복사본을 원하면 copy (또는 원하는 것에 따라 deepcopy )를 사용한 다음 업데이트하십시오. 두 줄의 코드는 .items() + .items()로 한 줄을 만드는 것보다 훨씬 더 읽기 쉽고 - 더 Pythonic합니다. 명시적인 것이 암시적인 것보다 낫습니다.

또한 .items()(Python 3.0 이전)를 사용할 때 사전의 항목을 포함하는 새 목록을 생성합니다. 사전이 크면 상당한 오버헤드가 발생합니다(병합된 사전이 생성되자마자 버려지는 두 개의 큰 목록). 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()는 이전 버전에서 작동합니다.



답변자 : zaphod


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

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

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

예 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분의 1로 승리했습니다. 제 책에서는 꽤 큰 승리입니다!

이 두 가지를 비교한 후 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 또는 y를 수정하는 대신 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

즉, z0z2 는 본질적으로 동일한 성능을 보인다. 이것이 우연의 일치라고 생각하십니까? 나는하지 않는다....

사실, 순수한 Python 코드가 이보다 더 나은 작업을 수행하는 것은 불가능하다고 주장하기까지 했습니다. 그리고 C 확장 모듈에서 훨씬 더 나은 작업을 수행할 수 있다면 Python 사용자가 코드(또는 접근 방식의 변형)를 Python 코어에 통합하는 데 관심이 있을 것이라고 생각합니다. 파이썬은 많은 곳에서 dict 운영을 최적화하는 것은 매우 중요합니다.

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

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

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



답변자 : Raymond Hettinger


Python 3.0 이상에서는 여러 dict 또는 기타 매핑을 그룹화하여 업데이트 가능한 단일 보기를 생성하는 collections.ChainMap 을 사용할 수 있습니다.

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

Python 3.5 이상용 업데이트 : PEP 448 확장 사전 패킹 및 풀기를 사용할 수 있습니다. 이것은 빠르고 쉽습니다.

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

Python 3.9 이상용 업데이트 : PEP 584 통합 연산자를 사용할 수 있습니다.

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


답변자 : rcreswick


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

 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


답변자 : Stan


재귀적으로/딥 업데이트 딕셔너리

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

수정해주셔서 감사합니다.



답변자 : Bilal Syed Hussain


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}

Python 3.9에서는 | 및 |= PEP 584의 아래 예와 함께

 d = {'spam': 1, 'eggs': 2, 'cheese': 3} e = {'cheese': 'cheddar', 'aardvark': 'Ethel'} d | e # {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}


답변자 : driax


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

 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) 만큼 빠르지는 않습니다. n = copy(a); n.update(b) , 적어도 CPython에서. iteritems() 를 2to3 도구에 의해 자동으로 수행되는 items() 변경하면 이 버전은 Python 3에서도 작동합니다.

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



답변자 : Greg Hewgill


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

두 사전('b') 모두에 키가 있는 항목의 경우 마지막에 입력하여 출력에서 끝나는 항목을 제어할 수 있습니다.



답변자 : phobie


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

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

위에서 언급한 z0과 사악한 z2만큼 빠르지만 이해와 변경이 쉽다.



답변자 : Sam Watkins


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

그런 모호하고 모호한 답변 중에서 이 빛나는 예제는 평생 독재자 Guido van Rossum이 보증한 Python에서 dicts를 병합하는 유일한 좋은 방법입니다! 다른 누군가가 이것의 절반을 제안했지만 함수에 넣지 않았습니다.

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

제공:

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


답변자 : EMS


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

 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}

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



답변자 : Robino


파이썬적이 되십시오. 이해력을 사용하십시오:

 z={i:d[i] for d in [x,y] for i in d} >>> print z {'a': 1, 'c': 11, 'b': 10}


답변자 : beardc


python3에서 items 메소드 는 더 이상 list 를 반환하지 않고 오히려 set처럼 작동 하는 view 를 반환합니다. + 연결하면 작동하지 않으므로 집합 조합을 사용해야 합니다.

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

버전 2.7의 python3과 같은 동작의 경우 viewitems items 대신 작동해야 합니다.

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

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

편집하다:

파이썬 3. 첫째, 노트에 대한 몇 가지 더 많은 포인트는 것을 dict(x, **y) 트릭은의 키 파이썬 3에서하지 작동하지 않는 y 문자열입니다.

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

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

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

 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의 팬이지만 많은 조회가 있을 수 있는 곳에서는 덜 실용적으로 보입니다.



답변자 : Mathieu Larose


두 개의 사전

 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/ 참조



답변자 : reubano


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

 # py2 from itertools import chain, imap merge = lambda *args: dict(chain.from_iterable(imap(dict.iteritems, args))) # py3 from itertools import chain merge = lambda *args: dict(chain.from_iterable(map(dict.items, 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}


답변자 : Claudiu


Matthew의 답변에 대한 단일 표현 솔루션으로 이어지는 남용:

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


답변자 : Thanh Lim


이 얕은 사전에 대한 답변은 좋았지만 여기에 정의된 방법 중 어느 것도 실제로 깊은 사전 병합을 수행하지 않습니다.

예는 다음과 같습니다.

 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' 항목은 'depth_2' 및 'extra' 항목이 실제로 병합인 경우 사전 내부 항목으로 있어야 합니다.

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

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

결과:

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

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

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



답변자 : gilch


x 변경하는 것이 마음에 들지 않으면

 x.update(y) or x

간단하고 읽기 쉽고 성능이 뛰어납니다. update() 항상 false 값인 None 반환한다는 것을 알고 있습니다. 따라서 위의 표현식은 업데이트 후 x

표준 라이브러리의 대부분의 변경 메서드(예: .update() ) None 을 반환하므로 이러한 종류의 패턴도 작동합니다. 그러나 dict 하위 클래스 또는 이 규칙을 따르지 않는 다른 메서드를 사용하는 경우 원하는 것이 아닐 수도 있는 왼쪽 피연산자를 반환 or 대신 첫 번째 요소가 평가하는 것과 상관없이 작동하는 튜플 표시 및 색인을 사용할 수 있습니다(비록 그다지 예쁘지는 않지만).

 (x.update(y), x)[-1]

변수에 아직 x 가 없는 경우 할당문을 사용하지 않고 lambda 이는 lambda 를 let 표현식 으로 사용하는 것과 같습니다. 이는 함수형 언어에서 흔히 볼 수 있는 기술이지만 비파이썬적일 수 있습니다.

 (lambda x: x.update(y) or x)({'a': 1, 'b': 2})

새로운 walrus 연산자(Python 3.8+ 전용)를 다음과 같이 사용하는 것과 크게 다르지는 않지만,

 (x := {'a': 1, 'b': 2}).update(y) or x

특히 기본 인수를 사용하는 경우:

 (lambda x={'a': 1, 'b': 2}: x.update(y) or x)()

사본을 원하시면 PEP 584 스타일 x | y 는 3.9+에서 가장 Pythonic입니다. 이전 버전을 지원해야 하는 경우 PEP 448 스타일 {**x, **y} 가 3.5 이상에서 가장 쉽습니다. 그러나 (더 오래된) Python 버전에서 사용할 수 없는 경우 let 표현식 패턴도 여기에서 작동합니다.

 (lambda z=x.copy(): z.update(y) or z)()

(z := x.copy()).update(y) or z 와 거의 동일하지만 Python 버전이 이에 대해 충분히 새롭다면 PEP 448 스타일을 사용할 수 있습니다.)



답변자 : xjcl


Python 3.9의 새로운 기능 : 합집합 연산자( | set s와 유사한 dict s를 병합합니다.

 >>> d = {'a': 1, 'b': 2} >>> e = {'a': 9, 'c': 3} >>> d | e {'a': 9, 'b': 2, 'c': 3}

일치하는 키의 경우 오른쪽 dict 이 우선 합니다.

dict 를 제자리에서 수정하기 |= 에 대해 작동합니다.

 >>> e |= d # e = e | d >>> e {'a': 1, 'c': 3, 'b': 2}


답변자 : GetFree


.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


답변자 : kjo


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

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

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

( dict.update 는 성공 시 None 을 반환하기 때문에 lambda or a 비트가 필요합니다.)



답변자 : Bijou Trouvaille


여기와 다른 곳에서 아이디어를 바탕으로 기능을 이해했습니다.

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

사용법(파이썬 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})

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



답변자 : upandacross


지금까지 나열된 솔루션에 대한 문제는 병합된 사전에서 키 "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}


답변자 : reetesh11


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

이렇게 하면 문제가 해결됩니다.



답변자 : ShadowRanger


PEP 572: Assignment Expressions 덕분에 Python 3.8 릴리스(2019년 10월 20일 예정 )에 새로운 옵션이 제공됩니다. 새로운 대입 표현식 연산자 := copy 의 결과를 할당하고 update 를 호출하는 데 계속 사용할 수 있습니다.

 newdict = dict1.copy() newdict.update(dict2)

에게:

 (newdict := dict1.copy()).update(dict2)

모든 면에서 동일하게 행동하면서. 당신은 또한 결과 반환해야하는 경우 dict (당신은 반환 식을 요청 dict , 위의 (가) 작성 및 양수인에 newdict 당신은 그대로 함수에 인수를 전달하는 데 사용할 수 있도록,하지만 반환하지 않습니다, a la myfunc((newdict := dict1.copy()).update(dict2)) ), 마지막에 or newdict updateNone newdict 평가하고 newdict 를 결과로 반환합니다. 식):

 (newdict := dict1.copy()).update(dict2) or newdict

중요한 주의 사항: 일반적으로 다음을 위해 이 접근 방식을 권장하지 않습니다.

 newdict = {**dict1, **dict2}

압축 풀기 접근 방식은 더 명확하고(처음에 일반화된 압축 풀기에 대해 알고 있는 사람에게는 그렇게 해야 함 ) 결과에 대한 이름이 전혀 필요하지 않습니다(따라서 즉시 전달되는 임시를 구성할 때 훨씬 더 간결합니다. 함수 또는 list / tuple 리터럴 등에 포함됨), 거의 확실하게 더 빠르며 (CPython에서) 다음과 대략 동일합니다.

 newdict = {} newdict.update(dict1) newdict.update(dict2)

dict API를 사용하여 C 계층에서 수행되므로 동적 메서드 조회/바인딩 또는 함수 호출 디스패치 오버헤드가 포함되지 않습니다(여기서 (newdict := dict1.copy()).update(dict2) 는 불가피하게 원본과 동일합니다. 동적 조회/바인딩/메소드 호출을 사용하여 개별 단계로 작업을 수행하는 동작의 두 줄.

dict 를 병합하는 것이 분명하므로 더 확장 가능합니다.

 newdict = {**dict1, **dict2, **dict3}

할당 표현식을 사용하면 그렇게 확장되지 않습니다. 가장 가까운 것은 다음과 같습니다.

 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)

None 의 임시 튜플이 None 결과에 대한 진실성 테스트가 있습니다.

 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)

둘 중 하나는 분명히 훨씬 더 못생겼고 더 많은 비효율성을 포함합니다(쉼표 분리를 위해 None tupleupdateNone 반환 or 분리에 대한 무의미한 진실성 테스트).

할당 표현식 접근 방식의 유일한 진정한 이점은 다음과 같은 경우에 발생합니다.

  1. setdict 모두 처리해야 하는 일반 코드가 있습니다 (둘 모두 copyupdate 지원하므로 코드는 예상대로 대략적으로 작동합니다).
  2. dict 자체뿐만 아니라 임의의 dict-like 객체를 받을 것으로 예상 하고 왼쪽의 유형과 의미를 보존해야 합니다 (일반 dict 끝나는 대신). myspecialdict({**speciala, **specialb}) 가 작동할 수 있지만 추가 임시 dict myspecialdict dict 가 보존할 수 없는 기능이 있는 경우 dict 이제 첫 번째 출현을 기반으로 순서를 보존합니다. 키의 마지막 모양에 따라 키 및 값, 당신은 하나를 할 수 있습니다 보존이 값을 업데이트하는 것도 끝까지 이동 있도록 키의 마지막 모양에 따라 주문하도록), 그 의미는 잘못된 것입니다. 할당 표현식 버전은 명명된 메서드(적절하게 동작하도록 오버로드된 것으로 추정됨)를 사용하기 때문에 dict1 이 이미 dict 가 아닌 경우 dict 를 전혀 생성하지 않고 원래 유형(및 원래 유형의 의미론)을 유지하면서 모든 임시.


답변자 : RemcoGerlich


이것은 단일 dict 이해로 수행할 수 있습니다.

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

제 생각에는 '단일 표현' 부분에 대한 최선의 대답은 추가 기능이 필요하지 않고 짧기 때문입니다.



출처 : Here


출처 : http:www.stackoverflow.com/questions/38987/how-do-i-merge-two-dictionaries-in-a-single-expression-taking-union-of-dictiona">

반응형