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




python hashmap (24)

두 개의 파이썬 사전을 가지고 있으며이 두 사전을 반환하는 단일 표현식을 작성하여 병합하려고합니다. dict in-place를 수정하는 대신 결과를 반환하는 경우 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을 어떻게 얻을 수 있습니까?

( dict.update() 의 최후의 하나의 충돌 처리가 dict.update() 내가 찾고있는 것입니다.)


비슷한 것을 원했지만 중복 키의 값이 병합 된 방법을 지정하는 기능을 사용하여이를 해킹했습니다 (그러나 심각하게 테스트하지는 않았습니다). 분명히 이것은 하나의 표현식이 아니지만 단일 함수 호출입니다.

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

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

z = dict(x, **y)

참고 : 이것은 대중적인 대답이되었지만, y 가 문자열이 아닌 키를 가지고 있다면, 이것이 작동한다는 사실은 CPython 구현 세부 사항의 남용이며 파이썬 3에서는 작동하지 않는다는 것을 지적하는 것이 중요합니다 , 또는 PyPy, IronPython 또는 Jython에서. 또한 Guido는 팬이 아닙니다 . 따라서 포워드 호환 또는 교차 구현 이식 코드에 대해서는이 기술을 추천 할 수 없으므로 완전히 피하는 것이 좋습니다.


지금까지 나와있는 솔루션으로 문제가되는 것은 병합 된 사전에서 키 "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}

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

예제는 다음과 같습니다.

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에서 제공 한 깊은 병합도 같은 결과를 만듭니다.

예, 샘플 사전을 병합하는 데 사용할 수 있지만 병합 할 일반적인 메커니즘은 없습니다. 진정한 병합을 수행하는 메소드를 작성한 후에는 나중에이를 업데이트하겠습니다.


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

재귀 적 / 깊은 업데이트 dict

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

수정에 대한 rednaw에게 감사드립니다.


이것은 아마 대중적인 대답은 아니지만, 거의 확실하게 이것을 원하지는 않을 것입니다. 병합이 필요한 사본을 원한다면 copy (또는 원하는 것에 따라 deepcopy )를 사용하고 업데이트하십시오. .items () + .items ()를 사용한 단일 행 작성보다 두 줄의 코드가 훨씬 더 읽기 쉽습니다. 암시 적보다 암묵적인 것이 낫습니다.

또한 .items () (Python 3.0 이전)를 사용하면 dict의 항목이 포함 된 새 목록이 생성됩니다. 사전이 큰 경우 오버 헤드가 많이 발생합니다 (병합 된 사전이 만들어지면 두 개의 큰 목록이 삭제됩니다). update ()는 두 번째 dict 항목을 통해 실행할 수 있기 때문에보다 효율적으로 작업 할 수 있습니다.

time 관점에서 :

>>> 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 ()는 이전 버전에서 작동합니다.


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

new = dict_merge(old, extras)

그러한 그늘지고 모호한 답변 가운데서,이 빛나는 예제는 독재자가 Guido van Rossum의 삶에 대해 인정한 Python에서 사전을 병합하는 유일한 방법입니다! 누군가 다른 사람의 제안을했지만 기능에 넣지는 않았습니다.

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

제공 :

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

python3에서 items 메소드 는 더 이상리스트를 리턴하지 않고 , 세트처럼 작동하는 뷰를 리턴합니다 . 이 경우 + 연결하면 작동하지 않습니다. 따라서 조합을 가져와야합니다.

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

버전 2.7에서의 python3와 비슷한 동작의 경우, items 대신 viewitems 메소드가 작동해야 items .

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

나는이 표기법을 어쨌든 선호하기 때문에 (표제에서 보여 주듯이) 연결보다는 집합 된 조합 동작으로 생각하는 것이 자연스러운 것처럼 보입니다.

편집하다:

파이썬 3을위한 몇 가지 더 많은 점. 먼저, dict(x, **y) 의 키가 문자열이 아니라면 dict(x, **y) 트릭은 파이썬 3에서 작동하지 않는다는 것에 주목하자.

또한 Raymond Hettinger의 Chainmap answer 은 임의의 수의 dict를 인수로 취할 수 있기 때문에 매우 우아합니다. 그러나 docs 에서는 각 조회에 대한 모든 dicts의 목록을 순차적으로 살펴 보는 것처럼 보입니다.

조회는 키가 발견 될 때까지 기본 맵핑을 연속적으로 검색합니다.

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

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

그래서 조회의 속도가 느려집니다. 나는 Chainmap의 팬이지만, 많은 조회가있을 수있는 곳에서는 실용적이지 않습니다.


이것은 하나의 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)
    }

필자의 견해로는 '단일 표현'부분에 대한 최선의 대답은 추가 기능이 필요하지 않으므로 짧습니다.



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

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

이렇게하면 원하는대로 마지막 문장을 z 에 넣고 두 번째 ( y ) 사전의 값에 의해 키 b 의 값이 제대로 재정의됩니다.

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

파이썬 3을 사용한다면 좀 더 복잡합니다. z 를 만들려면 :

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

두 개의 파이썬 사전을 하나의 표현식으로 병합하려면 어떻게해야합니까?

사전 xy , z 는 병합 된 사전이되고 y 값은 y 값을 대체합니다.

  • 파이썬 3.5 이상에서는 :

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • 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 )을 가져오고 두 번째 딕트의 값은 첫 번째 딕셔너리의 값을 첫 번째 겹쳐서 덮어 쓰게됩니다.

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

PEP 448 에서 제안되었고 Python 3.5 에서 사용 가능한 새로운 구문은 다음과 같습니다.

z = {**x, **y}

그리고 그것은 실제로 하나의 표현입니다. 이제 3.5, PEP 478출시 일정에 따라 구현되었으며, 이제는 Python 3.5 문서 의 새로운 기능에 들어갔다.

그러나 많은 조직이 아직 Python 2를 사용하고 있으므로 역 호환 방식으로이 작업을 수행 할 수 있습니다. Python 2와 Python 3.0-3.4에서 사용할 수있는 고전적인 파이썬 적 방법은 이것을 2 단계 프로세스로 수행하는 것입니다.

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

두 접근법 모두에서 y 는 두 번째로 올 것이고 그 값은 x 의 값을 대체 할 것이므로 'b' 는 최종 결과에서 3 을 가리킬 것입니다.

아직 파이썬 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)

정의되지 않은 수의 dicts를 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

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

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

g 키 값 쌍이 a 에서 f 까지의 dicts보다 우선 순위가 높습니다.

다른 답변 비판

이전에 수락 된 답변에서 볼 수있는 것을 사용하지 마십시오.

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

파이썬 2에서는 각 dict에 대해 메모리에 두 개의 목록을 만들고, 처음 두 개의 길이를 합친 길이의 메모리로 세 번째 목록을 만든 다음 세 개의 목록을 모두 삭제하여 dict을 만듭니다. 파이썬 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())) 예 : z = dict(list(x.items()) + list(y.items())) . 이것은 자원과 계산 능력의 낭비입니다.

마찬가지로 Python 3 (Python 2.7의 viewitems() items() 에서 items() 의 유니온을 취하는 것은 값이 unhashable 객체 (예 :리스트) 일 때도 실패합니다. 값이 해시 가능한 경우에도 집합은 의미 적으로 순서가 지정되지 않으므로 우선 순위와 관련하여 동작이 정의되지 않습니다. 그러지 마라.

>>> 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 생성자를 사용하며 매우 빠르고 메모리 효율적입니다 (두 단계 프로세스보다 조금 더 많습니다). 그러나 여기서 일어나는 일을 정확하게 알지 못하면 (즉, 두 번째 dict가 키워드 인수로 전달됩니다. dict 생성자), 읽기가 어렵습니다. 의도 한 용도가 아니므로 Pythonic이 아닙니다.

다음 은 장고 에서 수정 된 사용 예입니다.

Dicts는 해시 가능 키 (예 : frozensets 또는 tuples)를 사용하기위한 것이지만 키가 문자열이 아닌 경우이 메소드는 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(**y) . 예 :

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

대신에

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

의견에 대한 응답

귀도 (Guido)의 말에도 불구하고 dict(x, **y) 는 dict 사양과 일치합니다. 는 파이썬 2와 3 모두에서 작동합니다. 이것은 단지 문자열 키에서만 작동한다는 사실은 키워드 매개 변수가 작동하는 방식과 짧은 명령 방식이 아닌 직접적인 결과입니다. 이 곳에서 ** 연산자를 사용하는 것은 메커니즘의 남용입니다. 사실 **는 dicts를 키워드로 전달하도록 설계되었습니다.

다시 말하지만, 키가 비 문자열 일 때 3에 대해서는 작동하지 않습니다. 암묵적 호출 계약은 네임 스페이스가 일반적인 dicts를 사용하는 반면 사용자는 문자열 인 키워드 인수 만 전달해야한다는 것입니다. 다른 모든 호출자가이를 시행했습니다. 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)을 감안할 때 좋지 않습니다. 따라서 파이썬 3에서는이 사용법이 큰 변화가 될 수 있으므로 수정되었습니다.

나는 특정 버전의 언어에서만 작동하거나 특정 임의의 제약 조건 하에서 만 작동하는 코드를 의도적으로 작성하는 것은 악의적 인 무능함을 귀하에게 제시합니다.

또 다른 코멘트 :

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

내 답변 : merge_two_dicts(x, y) 는 실제로 우리가 가독성에 대해 염려한다면 사실 내게 훨씬 더 명확 해 보입니다. 파이썬 2는 점차 사용되지 않으므로 앞으로 호환되지 않습니다.

덜 수행하지만 올바른 Ad-hocs

이러한 접근 방식은 성능이 떨어지지 만 올바른 동작을 제공합니다. 상위 수준의 추상화 수준에서 각 키 - 값 쌍을 반복하기 때문에 copyupdate 또는 새 언 패킹 보다 성능이 훨씬 떨어지지 만 우선 순위를 존중합니다 (후자의 우선 순위가 우선 함)

또한 dict comprehension 내에서 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

다음은 우분투 14.04에서 수행됩니다.

Python 2.7 (시스템 파이썬) :

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

사전 관련 자료


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

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에서 n = copy(a); n.update(b) . 이 버전은 iteritems()items() 로 변경하면 Python 3에서도 작동합니다.이 2to3 도구는 자동으로 수행됩니다.

개인적으로 나는이 버전이 가장 좋다고 생각한다. 왜냐하면 그것은 하나의 기능적 구문에서 내가 원하는 것을 상당히 잘 설명하기 때문이다. 유일한 사소한 문제는 y의 값이 x의 값보다 우선한다는 것을 완전히 분명히하지는 못하지만, 그 점을 이해하는 것이 어렵지 않다고 생각합니다.


순서를 보존하는 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}

비단뱀이 되라. comprehension 사용하십시오.

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

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

대안:

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

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


문제는 이미 여러 번 답변되었지만 문제에 대한이 간단한 해결책은 아직 열거되지 않았습니다.

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

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


파이썬 2의 경우 :

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

파이썬 3 :

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

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


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

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

파이썬 3.5에서는 언팩 (unpack) **을 사용 하여 새로운 사전을 생성 할 수 있습니다 . 이 방법은 과거의 대답에는 나타나지 않았습니다. 또한 {}대신 사용하는 것이 좋습니다 dict(). 왜냐하면 {}파이썬 리터럴이고 dict()함수 호출이 필요하기 때문입니다.

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

후속 답변에서 이러한 두 가지 대안의 상대적인 성능에 대해 질문했습니다.

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

내 컴퓨터에서 적어도 Python 2.5.2를 실행하는 보통 x86_64 인 z2 는 짧고 단순 할뿐만 아니라 훨씬 빠릅니다. 파이썬과 함께 제공되는 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 것처럼 보이지만 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 z2 됩니다. 내 책에서 꽤 큰 승리입니다!

이 두 가지를 비교 한 후에 z1 의 성능 저하가 두 항목 목록을 구성하는 오버 헤드에 기인 할 수 있는지 궁금해했습니다. 그 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

z3z1 보다 다소 빠르지 만 z2 만큼 빠르다고 결론 내릴 수 있습니다. 모든 여분의 타이핑을할만한 가치는 없습니다.

이 토론은 여전히 ​​중요합니다. 이는 두 가지 목록을 병합하는 "분명한"방법, 즉 update 방법을 사용하여 이러한 대체 방법을 성능 비교하는 것입니다. 어떤 식 으로든 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 는 본질적으로 동일한 성능을 갖는 것처럼 보입니다. 우연의 일치라고 생각하십니까? 나는 ....하지 않는다.

사실, 필자는 순수 파이썬 코드가 이것보다 더 훌륭하게 할 수 없다고 주장하기까지했다. 그리고 C 확장 모듈에서 훨씬 더 잘할 수 있다면, 파이썬 사람들은 파이썬 코어에 코드 (또는 접근법 변형)를 포함시키는 것에 관심이있을 것이라고 생각합니다. 파이썬은 많은 장소에서 dict 를 사용합니다. 운영 최적화는 큰 문제입니다.

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

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

Tony가 그렇듯이 표기법의 차이가 성능에 어떤 측정 가능한 영향을 미치지 않는 것으로 나타났습니다. 당신에게 맞는 모양을 사용하십시오. 물론, 그는 두 문장의 버전이 훨씬 이해하기 쉽다는 것을 지적하는 것은 옳다.


여기에 아이디어를 그리거나 다른 곳에서 나는 함수를 이해했다.

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

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







merge