unir - update diccionario python




¿Cómo fusionar dos diccionarios en una sola expresión? (20)

Tengo dos diccionarios de Python y quiero escribir una sola expresión que devuelva estos dos diccionarios, combinados. El método update() sería lo que necesito, si devolviera su resultado en lugar de modificar un dict en el lugar.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

¿Cómo puedo obtener ese dictado final fusionado en z , no x ?

(Para ser más claro, el manejo de conflictos dict.update() es lo que estoy buscando).


¿Cómo puedo combinar dos diccionarios de Python en una sola expresión?

Para los diccionarios x e y , z convierte en un diccionario combinado con valores de y reemplaza a los de x .

  • En Python 3.5 o superior,:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • En Python 2, (o 3.4 o inferior) escribe una función:

    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
    

    y

    z = merge_two_dicts(x, y)
    

Explicación

Supongamos que tiene dos dictados y desea fusionarlos en un nuevo dict sin alterar los dictados originales:

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

El resultado deseado es obtener un nuevo diccionario ( z ) con los valores combinados, y los valores del segundo dict que sobrescriben los del primero.

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

Una nueva sintaxis para esto, propuesta en PEP 448 y disponible a partir de Python 3.5 , es

z = {**x, **y}

Y de hecho es una sola expresión. Ahora se muestra como implementado en el calendario de lanzamiento para 3.5, PEP 478 , y ahora se ha introducido en el documento Novedades en Python 3.5 .

Sin embargo, dado que muchas organizaciones aún están en Python 2, es posible que desee hacer esto de una manera compatible hacia atrás. La forma clásica de Pythonic, disponible en Python 2 y Python 3.0-3.4, es hacer esto como un proceso de dos pasos:

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

En ambos enfoques, y vendrá en segundo lugar y sus valores reemplazarán a los valores de x , por lo que 'b' apuntará a 3 en nuestro resultado final.

Aún no está en Python 3.5, pero desea una sola expresión

Si aún no está en Python 3.5, o necesita escribir código compatible con versiones anteriores, y quiere esto en una sola expresión , el método más eficaz y correcto es ponerlo en una función:

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

y entonces tienes una sola expresión:

z = merge_two_dicts(x, y)

También puede hacer que una función fusione un número indefinido de dictados, de cero a un número muy grande:

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

Esta función funcionará en Python 2 y 3 para todos los dictados. por ejemplo, dados dados g :

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

y los pares de valores clave en g tendrán prioridad sobre los dictados a a f , y así sucesivamente.

Críticas de otras respuestas

No uses lo que ves en la respuesta aceptada anteriormente:

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

En Python 2, crea dos listas en la memoria para cada dictado, crea una tercera en la memoria con una longitud igual a la longitud de las dos primeras juntas y luego descarta las tres listas para crear el dict. En Python 3, esto fallará porque estás agregando dos objetos dict_items juntos, no dos listas -

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

y tendría que crearlos explícitamente como listas, por ejemplo, z = dict(list(x.items()) + list(y.items())) . Esto es un desperdicio de recursos y poder de cómputo.

Del mismo modo, tomar la unión de items() en Python 3 ( viewitems() en Python 2.7) también fallará cuando los valores sean objetos no lavables (como listas, por ejemplo). Incluso si sus valores son hashables, dado que los conjuntos no están ordenados semánticamente, el comportamiento no está definido en lo que respecta a la precedencia. Así que no hagas esto:

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

Este ejemplo demuestra lo que sucede cuando los valores son inestables:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Aquí hay un ejemplo donde y debería tener prioridad, pero en cambio, el valor de x se conserva debido al orden arbitrario de los conjuntos:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

Otro truco que no debes usar:

z = dict(x, **y)

Esto utiliza el constructor dict , y es muy rápido y eficiente en memoria (incluso un poco más que nuestro proceso de dos pasos) pero a menos que sepa exactamente lo que está sucediendo aquí (es decir, el segundo dictado se pasa como argumentos de palabras clave a la dict constructor), es difícil de leer, no es el uso previsto y, por lo tanto, no es Pythonic.

Aquí hay un ejemplo del uso que se está remediando en django .

Los dictados están diseñados para tomar claves hashable (por ejemplo, frozensets o tuplas), pero este método falla en Python 3 cuando las claves no son cadenas.

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

Desde la lista de correo , Guido van Rossum, el creador del lenguaje, escribió:

Estoy bien con declarar que dict ({}, ** {1: 3}) es ilegal, ya que después de todo es un abuso del mecanismo **.

y

Aparentemente, dict (x, ** y) está dando la vuelta como "truco genial" para "llamar x.update (y) y devolver x". Personalmente me parece más despreciable que cool.

Es mi entendimiento (así como la comprensión del creador del lenguaje ) que el uso previsto para dict(**y) es para crear dictados con fines de legibilidad, por ejemplo:

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

en lugar de

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

Respuesta a los comentarios.

A pesar de lo que dice Guido, dict(x, **y) está en línea con la especificación de dict, que por cierto. funciona tanto para Python 2 como para 3. El hecho de que esto solo funcione para claves de cadena es una consecuencia directa de cómo funcionan los parámetros de palabras clave y no un corto de dictado. El uso del operador ** en este lugar tampoco es un abuso del mecanismo, de hecho ** fue diseñado precisamente para pasar los dictados como palabras clave.

Nuevamente, no funciona para 3 cuando las claves no son cadenas. El contrato de llamada implícito es que los espacios de nombres toman dictados ordinarios, mientras que los usuarios solo deben pasar los argumentos de palabras clave que son cadenas. Todos los demás callables lo hicieron cumplir. dict rompió esta consistencia en 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}

Esta inconsistencia fue mala dadas otras implementaciones de Python (Pypy, Jython, IronPython). Por lo tanto, se solucionó en Python 3, ya que este uso podría ser un cambio importante.

Le aseguro que es una incompetencia maliciosa escribir código intencionalmente que solo funciona en una versión de un idioma o que solo funciona con ciertas restricciones arbitrarias.

Otro comentario:

dict(x.items() + y.items()) sigue siendo la solución más legible para Python 2. La legibilidad cuenta.

Mi respuesta: merge_two_dicts(x, y) realidad me parece mucho más claro, si realmente nos preocupa la legibilidad. Y no es compatible con versiones posteriores, ya que Python 2 está cada vez más en desuso.

Menos performantes pero correctos ad-hocs

Estos enfoques son menos eficaces, pero proporcionarán un comportamiento correcto. Tendrán un rendimiento mucho menor que la copy y la update o el nuevo desempaquetado porque iteran a través de cada par clave-valor en un nivel más alto de abstracción, pero respetan el orden de prioridad (estos últimos tienen prioridad)

También puede encadenar los dictados manualmente dentro de una comprensión de dictado:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

o en Python 2.6 (y quizás tan pronto como 2.4 cuando se introdujeron las expresiones del generador):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain encadenará los iteradores sobre los pares clave-valor en el orden correcto:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

Análisis de rendimiento

Solo voy a hacer el análisis de rendimiento de los usos conocidos para comportarse correctamente.

import timeit

Lo siguiente se hace en Ubuntu 14.04.

En Python 2.7 (sistema 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

En Python 3.5 (PPA muertos):

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

Recursos en Diccionarios


Actualización recursiva / profunda de un 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

Demostración:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

Salidas:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

Gracias rednaw para las ediciones.


Aprovechando ideas aquí y en otros lugares, he comprendido una función:

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

Uso (probado en python 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})

Podrías usar un lambda en su lugar.


El abuso que conduce a una solución de una sola expresión para la respuesta de 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}

Dijiste que querías una expresión, así que abusé de lambda para unir un nombre y tuplas para anular el límite de una expresión de lambda. Siéntete libre de temblar.

Por supuesto, también puede hacer esto si no le importa copiarlo:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

En Python 3, puede usar collections.ChainMap Mapa de la Cadena que agrupa varios dicts u otras asignaciones para crear una vista única y actualizable:

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


En tu caso, lo que puedes hacer es:

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

Esto, como usted lo desee, pondrá el dictado final en z , y hará que el valor de la clave b se anule correctamente por el valor del segundo dictado ( y ):

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

Si usas Python 3, es un poco más complicado. Para crear z :

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

En una respuesta de seguimiento, usted preguntó sobre el desempeño relativo de estas dos alternativas:

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

En mi máquina, al menos (un x86_64 bastante común que ejecuta Python 2.5.2), la alternativa z2 no solo es más corta y simple, sino que también es significativamente más rápida. Puedes verificar esto por ti mismo usando el módulo timeit que viene con Python.

Ejemplo 1: diccionarios idénticos que mapean 20 enteros consecutivos a sí mismos:

% 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 gana por un factor de 3.5 o menos. Diferentes diccionarios parecen producir resultados bastante diferentes, pero z2 siempre parece salir adelante. (Si obtiene resultados inconsistentes para la misma prueba, intente pasar -r con un número mayor que el valor predeterminado 3.)

Ejemplo 2: diccionarios no superpuestos que asignan 252 cadenas cortas a enteros y viceversa:

% 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 gana alrededor de un factor de 10. ¡Es una gran victoria en mi libro!

Después de comparar esos dos, me pregunté si el bajo rendimiento de z1 podría atribuirse a la sobrecarga de la construcción de las dos listas de elementos, lo que a su vez me llevó a preguntarme si esta variación podría funcionar mejor:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

Algunas pruebas rápidas, por ejemplo

% 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

me llevan a la conclusión de que z3 es algo más rápido que z1 , pero no tan rápido como z2 . Definitivamente no vale la pena todo el tipeo extra.

A esta discusión aún le falta algo importante, que es una comparación de rendimiento de estas alternativas con la forma "obvia" de combinar dos listas: mediante el método de update . Para tratar de mantener las cosas en pie de igualdad con las expresiones, ninguna de las cuales modifica x o y, haré una copia de x en lugar de modificarla en el lugar, de la siguiente manera:

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

Un resultado típico:

% 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

En otras palabras, z0 y z2 parecen tener un rendimiento esencialmente idéntico. ¿Crees que esto podría ser una coincidencia? Yo no....

De hecho, me atrevería a afirmar que es imposible que el código Python puro haga algo mejor que esto. Y si puedes hacerlo significativamente mejor en un módulo de extensión C, imagino que la gente de Python podría estar interesada en incorporar tu código (o una variación de tu enfoque) en el núcleo de Python. Python usa el dict en muchos lugares; optimizar sus operaciones es un gran problema.

También podrías escribir esto como

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

como lo hace Tony, pero (no sorprendentemente) la diferencia en la notación resulta que no tiene ningún efecto medible en el rendimiento. Usa lo que te parezca más adecuado. Por supuesto, es absolutamente correcto señalar que la versión de dos estados es mucho más fácil de entender.


Otra opción más concisa:

z = dict(x, **y)

Nota : esta se ha convertido en una respuesta popular, pero es importante señalar que si y tiene claves que no son de cadena, el hecho de que esto funcione es un abuso de un detalle de implementación de CPython, y no funciona en Python 3 , o en PyPy, IronPython, o Jython. Además, Guido no es un fan . Por lo tanto, no puedo recomendar esta técnica para el código portátil compatible o de implementación cruzada, lo que realmente significa que debe evitarse por completo.


Para Python 2:

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

Para Python 3:

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

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


Python 3.5 (PEP 448) permite una mejor opción de sintaxis:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

O incluso

final = {'a': 1, 'b': 1, **x, **y}

Quería algo similar, pero con la capacidad de especificar cómo se fusionaban los valores de las claves duplicadas, por lo que eliminé esto (pero no lo probé). Obviamente, esta no es una expresión única, pero es una llamada de una sola función.

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

Si bien la pregunta ya ha sido respondida varias veces, esta solución simple al problema aún no se ha incluido.

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

Es tan rápido como z0 y el mal z2 mencionado anteriormente, pero fácil de entender y cambiar.


Si crees que las lambdas son malas, entonces no sigas leyendo. Según lo solicitado, puede escribir la solución rápida y eficiente en memoria con una expresión:

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}

Como se sugirió anteriormente, usar dos líneas o escribir una función es probablemente una mejor manera de hacerlo.


Una alternativa:

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

(Solo para Python2.7 *; hay soluciones más simples para Python3 *).

Si no es reacio a importar un módulo de biblioteca estándar, puede hacerlo

from functools import reduce

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

(El or abit en el lambdaes necesario porque dict.updatesiempre vuelve Noneen éxito).


Esto se puede hacer con una sola comprensión de dictado:

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

En mi opinión, la mejor respuesta para la parte de 'expresión única' ya que no se necesitan funciones adicionales, y es breve.


En Python 3.5 puedes usar unpack **para crear un nuevo diccionario. Este método no se ha mostrado en las respuestas anteriores. Además, es mejor usar en {}lugar de dict(). Porque {}es un literal de python e dict()implica una llamada de función.

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

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

new = dict_merge(old, extras)

¡Entre esas respuestas sombrías y dudosas, este brillante ejemplo es la única y buena manera de fusionar los dictados en Python, respaldado por el mismo dictador vitalicio Guido van Rossum ! Alguien más sugirió la mitad de esto, pero no lo puso en una función.

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

da:

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

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

Esto debería solucionar tu problema.





merge