스택큐힙리스트

파이썬에서 두 개의 사전을 단일 표현으로 병합하는 방법은 무엇인가요? 본문

카테고리 없음

파이썬에서 두 개의 사전을 단일 표현으로 병합하는 방법은 무엇인가요?

스택큐힙리스트 2023. 3. 19. 05:28
반응형

나는 두 개의 사전을 하나의 새로운 사전으로 병합하고 싶습니다.

x = {'a': 1, 'b': 2}

y = {'b': 3, 'c': 4}

z = merge(x, y)

>>> z

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

두 사전에 모두 키 k이 있을 때에는 값 y[k]만 유지되어야 합니다.

답변 1

하나의 표현식으로 두 개의 파이썬 사전을 병합할 수 있나요?

사전 x 와 y!@ 의 얕게 병합된 사전 #$&##$$$& 는 y!@ 에서 값을 가져와 x 의 것을 대체합니다.

파이썬 3.9.0 이상 (2020년 10월 17일 출시, PEP-584 , discussed here )에서:

z = x | y

파이썬 3.5 이상에서:

z = {**x, **y}

파이썬 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

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

이에 대한 새로운 구문은 PEP 448 및 available as of Python 3.5에서 제안되었습니다.

z = {**x, **y}

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

리터럴 표기법으로 병합할 수 있다는 것에 유의하세요.

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

지금은:

>>> z

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

이제 release schedule for 3.5, PEP 478 에 적용되었으며 What's New in 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 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 객체를 더하기 때문입니다.

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

Traceback (most recent call last):

File , line 1, in

TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

당신은 명시적으로 이들을 목록으로 생성해야할 것입니다. 예 : z = dict(list(x.items()) + list(y.items())). 이것은 자원과 계산 능력의 낭비입니다.

Python 3에서 items()의 합집합을 취하는 것도 (Python 2.7의 viewitems()) 값이 해시 할 수 없는 객체 (예: 리스트)일 때 실패합니다. 값이 해시 가능하더라도 세트는 의미상 순서가 없으므로 우선 순위에 대해 정의되지 않은 동작입니다. 그러니 이렇게하지 마세요:

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

이 예제는 값이 해시할 수 없을 때 무슨 일이 일어나는지 보여줍니다.

>>> x = {'a': []}

>>> y = {'b': []}

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

Traceback (most recent call last):

File , line 1, in

TypeError: unhashable type: 'list'

여기에는 y가 우선해야 할 예가 있습니다. 그러나 세트의 임의 순서로 인해 x의 값을 유지합니다.

>>> x = {'a': 2}

>>> y = {'a': 1}

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

{'a': 2}

또 다른 해킹 방법은 사용하지 마십시오:

z = dict(x, **y)

이 코드는 dict 생성자를 사용하며 매우 빠르고 메모리 효율적입니다 (우리의 두 단계 프로세스보다 조금 더 효율적입니다). 그러나 두 번째 딕셔너리가 딕셔너리 생성자에 키워드 인자로 전달된다는 것을 정확히 알지 않으면 읽기 어렵고 의도한 사용 방법과 다르기 때문에 Pythonic하지 않습니다.

이것은 사용 예시 remediated in django 입니다.

사전은 해시 가능한 키 (예 : # $$ & @ # $ # $ & s 또는 튜플)를 사용하는 것이 목적이지만, Python 3에서는 키가 문자열이 아닐 때 이 메소드가 실패합니다.

>>> c = dict(a, **b)

Traceback (most recent call last):

File , line 1, in

TypeError: keyword arguments must be strings

mailing list에서 이 언어의 창시자인 Guido van Rossum이 씀:

** 메커니즘의 오용이기 때문에 dict ({}, ** {1 : 3})를 불법으로 선언하는 것에 대해 나는 괜찮습니다.

그리고

진행상황으로 보면 dict(x, **y)가 x.update(y)를 호출하고 x를 반환하는 멋진 해킹으로 알려져 있다. 개인적으로는 멋보다는 비열하다고 생각한다.

creator of the language와 저의 이해에 따르면 dict(**y)의 의도된 사용은 가독성 목적으로 사전을 만드는 것입니다. 예를 들어:

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

대신에

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

댓글에 대한 답변

가이도가 말하는 것과는 달리, #$ *& $ @@ * $ &은 dict 사양과 일치하며 두 Python 2 및 3에서 모두 작동한다. 이것이 문자열 키에 대해서만 작동하는 것은 키워드 매개변수가 작동하는 방식의 직접적인 결과이며 dict의 결함이 아니다. 또한 이 위치에서 ** 연산자를 사용하는 것은 메커니즘을 남용하는 것이 아니며 사실 **는 사전을 특히 키워드로 전달하기 위해 설계되었다.

다시 말하지만, 키가 문자열이 아닌 경우에는 3에 대해서는 작동하지 않습니다. 암시적 호출 계약은 네임스페이스가 보통의 사전을 사용하고, 사용자는 반드시 문자열인 키워드 인수만 전달해야 한다는 것입니다. 다른 모든 호출 가능한 객체들은 이를 강제하고 있습니다. dict은 이 일관성을 파이썬 2에서 깨트렸습니다.

>>> foo(**{('a', 'b'): None})

Traceback (most recent call last):

File , line 1, in

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

이 질문의 범위를 넘어서 다른 값 유형의 대책을 생각하는 것은 훨씬 범위를 벗어나므로, my answer to the canonical question on a Dictionaries of dictionaries merge 에서 안내해 드리겠습니다.

성능은 떨어지지만 정확한 Ad-hocs

이러한 접근 방식은 성능이 덜 우수하지만 올바른 동작을 제공합니다. 그들은 각 키-값 쌍을 더 높은 추상화 수준에서 반복하기 때문에 copy 및 update 또는 새로운 언팩킹보다 훨씬 성능이 저하될 것입니다. 그러나 그들은 우선 순위 순서를 존중합니다(나중에 나오는 딕셔너리가 우선순위가 있음).

당신은 수동으로도 딕셔너리를 체인할 수 있습니다. 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())))

파이썬 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

사전에 대한 자료

My explanation of Python's dictionary implementation, updated for 3.6.

Answer on how to add new keys to a dictionary

Mapping two lists into a dictionary

The official Python docs on dictionaries

The Dictionary Even Mightier - 브랜든 로드가 Pycon 2017에서 한 이야기

Modern Python Dictionaries, A Confluence of Great Ideas - Pycon 2017에서 Raymond Hettinger의 강연

답변 2

파이썬은 매우 강력한 언어이며, 딕셔너리도 이에 포함됩니다. 딕셔너리는 여러분이 다른 데이터 타입보다 더 많은 정보를 저장하기에 좋은 방법입니다. 그러나 때로는 여러 딕셔너리를 병합해야 할 때가 있습니다. 파이썬에서는 이를 하나의 표현으로 처리할 수 있습니다.

딕셔너리를 병합하는 방법에 대해 이야기하기 전에, 먼저 파이썬에서 딕셔너리가 무엇인지 알아보겠습니다. 딕셔너리는 파이썬에서 가장 중요한 데이터 타입 중 하나입니다. 딕셔너리는 여러 가지 데이터를 저장할 수 있으며, 각 데이터에 고유한 키를 할당할 수 있습니다. 이것은 검색 및 정보 처리에서 매우 유용한 도구입니다.

딕셔너리를 병합하는 방법은 매우 간단합니다. 하지만 많은 사람들이 이를 수행하는 방법을 알지 못합니다. 딕셔너리를 병합하는 가장 간단한 방법은 update() 메서드를 사용하는 것입니다.

예를 들어, 첫 번째 딕셔너리 d1 = {'a': 1, 'b': 2}와 두 번째 딕셔너리 d2 = {'c': 3, 'd': 4}가 있다고 가정해 봅시다. 이 두 딕셔너리를 병합하려면 다음과 같이 단일 표현으로 작성할 수 있습니다.

d3 = {**d1, **d2}

이렇게하면 d1과 d2의 모든 키와 값이 d3에 병합됩니다. 이 병합된 딕셔너리는 아래와 같이 출력됩니다.

d3 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

딕셔너리를 병합하는 또 다른 방법은 사용자 정의 함수를 만드는 것입니다. 이를 통해 더 구체적인 병합 행동을 정의할 수 있습니다.

이 way, Python users can easily merge their dictionaries into a single expression using update() or a custom function, and without having to write long pieces of code. This useful feature can save time and effort for developers who rely on dictionaries in their Python programs.

반응형
Comments