두 개의 파이썬 사전을 단일 표현식으로 병합하려면 어떻게해야합니까?
사전의 경우 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)
파이썬 3.9.0a4 이상 (최종 릴리스 날짜 2020 약 10월)에서 : PEP-584 , 여기서 논의 , 더이 문제를 단순화하기 위해 구현되었다 :
z = x | y # NOTE: 3.9+ ONLY
설명
두 개의 dicts가 있고 원래 dicts를 변경하지 않고 새 dict로 병합하려고한다고 가정하십시오.
x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
원하는 결과는 z
값이 병합 된 새 사전 ( ) 을 가져 오고 두 번째 dict의 값은 첫 번째 값을 덮어 씁니다.
>>> z
{'a': 1, 'b': 3, 'c': 4}
제안이에 대한 새로운 구문, PEP (448) 및 파이썬 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에서 사용할 수있는 고전적인 Pythonic 방식은 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)
정의되지 않은 수의 dict을 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
이 함수는 모든 dicts에 대해 Python 2 및 3에서 작동합니다. 예를 들면 다음 a
과 g
같습니다.
z = merge_dicts(a, b, c, d, e, f, g)
과에서 키 값 쌍은 g
dicts보다 우선합니다 a
에 f
, 등등.
다른 답변의 비판
이전에 받아 들여진 답변에 표시된 것을 사용하지 마십시오.
z = dict(x.items() + y.items())
Python 2에서는 각 dict에 대해 메모리에 두 개의 목록을 작성하고 처음 두 개의 길이와 같은 길이의 메모리에 세 번째 목록을 작성한 다음 세 목록을 모두 버리고 dict를 작성하십시오. 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()
파이썬 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에서 사용되는 사용법의 예입니다 .
딕셔너리는 해시 가능 키 (예 : 고정 세트 또는 튜플)를 사용하려고하지만 키가 문자열이 아닌 경우 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)는 "call x.update (y) and return x"에 대한 "cool hack"으로 돌아가고 있습니다. 개인적으로 나는 그것이 차가운 것보다 더 비열한 것을 안다.
의도 된 사용법 은 가독성을위한 사전 작성을 위한 것임을 이해하는 것입니다 ( 예 : 언어 작성자의 이해 ) dict(**y)
.
dict(a=1, b=10, c=11)
대신에
{'a': 1, 'b': 10, 'c': 11}
의견에 대한 답변
Guido의 말에도 불구하고 dict(x, **y)
dict 사양과 일치합니다. 이것은 파이썬 2와 3 모두에서 작동합니다. 이것은 문자열 키에서만 작동한다는 사실은 키워드 매개 변수가 작동하는 방식의 직접적인 결과이며 dict의 단점이 아닙니다. ** 대신에 ** 연산자를 사용하여 메커니즘을 남용하지 않습니다. 실제로 **는 dicts를 키워드로 전달하도록 정확하게 설계되었습니다.
키가 문자열이 아닌 경우에도 3에서 작동하지 않습니다. 암시 적 호출 계약은 네임 스페이스가 일반적인 dicts를 취하는 반면 사용자는 문자열 인 키워드 인수 만 전달해야한다는 것입니다. 다른 모든 소명은 그것을 시행했다. 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)의 다른 구현을 고려할 때이 불일치가 나빴습니다. 따라서이 사용법은 주요 변경 사항이 될 수 있으므로 Python 3에서 수정되었습니다.
한 버전의 언어에서만 작동하거나 특정 임의의 제약 조건에서만 작동하는 코드를 의도적으로 작성하는 것은 악의적 인 무능함이라고 귀하에게 제출합니다.
더 많은 의견 :
dict(x.items() + y.items())
여전히 파이썬 2에서 가장 읽기 쉬운 솔루션입니다.
내 대답 : merge_two_dicts(x, y)
실제로 가독성에 관심이 있다면 실제로 나에게 훨씬 분명해 보입니다. 그리고 파이썬 2가 점점 더 이상 사용되지 않으므로 앞으로 호환되지 않습니다.
{**x, **y}
중첩 된 사전을 처리하지 않는 것 같습니다. 중첩 된 키의 내용은 단순히 덮어 쓰지 않고 병합되지 않습니다. [...] 나는 재귀 적으로 병합되지 않는 이러한 답변에 의해 화상을 입었고 아무도 그것을 언급하지 않은 것에 놀랐습니다. "병합"이라는 단어를 해석 할 때이 답변은 "한 dict을 다른 dict로 업데이트"하고 병합하지 않는 것을 설명합니다.
예. 나는 두 개의 사전을 얕게 병합 하고 첫 번째 값 을 두 번째 값으로 덮어 씁니다.
두 개의 사전 사전을 가정하면 하나를 재귀 적으로 단일 함수로 병합 할 수 있지만 소스 중 하나에서 dicts를 수정하지 않도록주의해야하며 값을 지정할 때 사본을 작성하지 않아야합니다. 키는 해시 가능해야하며 일반적으로 불변이므로 키를 복사하는 것은 의미가 없습니다.
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
새로운 풀기가 더 높은 추상화 수준에서 각 키 - 값 쌍을 반복하기 때문에 나,하지만 그들은 할 우선 순위를 (후자 dicts이 우선 순위가) 존중
dict 이해 내에서 dicts를 수동으로 연결할 수도 있습니다.
{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7
또는 파이썬 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 = x | y