Pregunta

Tengo dos diccionarios de Python y quiero escribir una única expresión que devuelva estos dos diccionarios combinados.El update() El método sería lo que necesito, si devolviera su resultado en lugar de modificar un dictado 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? z, no x?

(Para ser más claro, el último en ganar es el manejo de conflictos dict.update() es lo que estoy buscando también).

¿Fue útil?

Solución

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

Para diccionarios x y y, z se convierte en un diccionario superficialmente fusionado con valores de y reemplazando a los de x.

  • En Python 3.5 o superior:

    z = {**x, **y}
    
  • 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 ahora:

    z = merge_two_dicts(x, y)
    

Explicación

Supongamos que tiene dos dictados y desea fusionarlos en un nuevo dictado 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 fusionados y los valores del segundo dict sobrescribiendo 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.

Tenga en cuenta que también podemos fusionarnos con notación literal:

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

y ahora:

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

Ahora se muestra como implementado en el calendario de lanzamiento para 3.5, PEP 478, y ahora ha llegado a Novedades de Python 3.5 documento.

Sin embargo, dado que muchas organizaciones todavía utilizan Python 2, es posible que desee hacerlo de forma compatible con versiones anteriores.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 pasará a segundo plano y sus valores sustituirán xlos valores, por lo tanto 'b' apuntará a 3 en nuestro resultado final.

Todavía no estoy en Python 3.5, pero quiero un expresión única

Si aún no estás en Python 3.5, o necesitas escribir código compatible con versiones anteriores y quieres esto en un expresión única, el enfoque 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 luego tienes una sola expresión:

z = merge_two_dicts(x, y)

También puedes crear una función para fusionar un número indefinido de dictados, desde cero hasta 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.p.ej.dictados dados a a g:

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

y pares clave-valor en g tendrá prioridad sobre los dictados a a f, etcétera.

Críticas de otras respuestas

No utilice lo que ve en la respuesta aceptada anteriormente:

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

En Python 2, crea dos listas en la memoria para cada dict, crea una tercera lista 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 sumando dos dict_items objetos 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ías que crearlos explícitamente como listas, p.e. z = dict(list(x.items()) + list(y.items())).Esto es un desperdicio de recursos y potencia de cálculo.

De manera similar, tomando la unión de items() en Python 3 (viewitems() en Python 2.7) también fallará cuando los valores sean objetos que no se pueden dividir (como listas, por ejemplo).Incluso si tus valores son hash, dado que los conjuntos están semánticamente desordenados, el comportamiento no está definido con respecto a la precedencia.Así que no hagas esto:

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

Este ejemplo demuestra lo que sucede cuando los valores no se pueden dividir en hash:

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

A continuación se muestra un ejemplo en el que 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 deberías usar:

z = dict(x, **y)

Esto utiliza el dict constructor, 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 dict se pasa como argumentos de palabras clave al constructor dict), es difícil de leer, no es el uso previsto y, por lo tanto, no es Pythonic.

A continuación se muestra un ejemplo del uso remediado en django.

Los dictados están destinados a aceptar claves hash (p. ej.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 el lista de correo, Guido van Rossum, el creador del idioma, escribió:

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

y

Aparentemente, dict (x, ** y) está dando vueltas como "Hack Cool" para "Llame a X.Update (y) y return x".Personalmente lo encuentro más despreciable que genial.

Es mi entendimiento (así como el entendimiento de la creador de la lengua) 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 comentarios

A pesar de lo que dice Guido, dict(x, **y) está en línea con la especificación 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 una deficiencia de dict.El uso del operador ** en este lugar tampoco es un abuso del mecanismo; de hecho, ** fue diseñado precisamente para pasar dicts 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 argumentos de palabras clave que sean cadenas.Todos los demás exigibles 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.

Les afirmo que es una incompetencia maliciosa escribir intencionalmente código que solo funciona en una versión de un idioma o que solo funciona bajo ciertas restricciones arbitrarias.

Más comentarios:

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) En 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 obsoleto.

{**x, **y} no parece manejar diccionarios anidados.el contenido de las claves anidadas simplemente se sobrescribe, no se fusiona [...] Terminé quemado por estas respuestas que no se fusionan de forma recursiva y me sorprendió que nadie lo mencionara.En mi interpretación de la palabra "fusionar", estas respuestas describen "actualizar un dictado con otro" y no fusionar.

Sí.Debo remitirles nuevamente a la pregunta, que pide una poco profundo fusión de dos diccionarios, donde los valores del primero se sobrescriben con los del segundo, en una sola expresión.

Suponiendo dos diccionarios de diccionarios, uno podría fusionarlos recursivamente en una sola función, pero debe tener cuidado de no modificar los dictados de ninguna de las fuentes, y la forma más segura de evitarlo es hacer una copia al asignar valores.Como las claves deben ser hash y, por lo tanto, normalmente son inmutables, no tiene sentido copiarlas:

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

Uso:

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

Proponer contingencias para otros tipos de valores está mucho más allá del alcance de esta pregunta, por lo que le señalaré mi respuesta a la pregunta canónica sobre "Fusionar diccionarios de diccionarios".

Ad-hocs menos eficaces pero correctos

Estos enfoques tienen menos rendimiento, pero proporcionarán un comportamiento correcto.Ellos estarán mucho menos más eficiente que copy y 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 hacer respetar el orden de precedencia (los últimos dictados tienen prioridad)

También puedes encadenar los dicts manualmente dentro de un dict por comprensión:

{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 ya en 2.4 cuando se introdujeron las expresiones generadoras):

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

Sólo voy a hacer el análisis de rendimiento de los usos que se sabe que se comportan 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 de serpientes muertas):

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

Otros consejos

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 haga el valor de la clave b ser anulado correctamente por el segundo (y) valor del dict:

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

Si usa Python 3, es sólo un poco más complicado.Crear z:

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

Una alternativa:

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

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 cadenas, el hecho de que esto funcione es un abuso de un detalle de implementación de CPython, y no funciona en Python 3, ni en PyPy, IronPython o Jython.También, Guido no es fan.Por lo tanto, no puedo recomendar esta técnica para código portátil compatible con versiones posteriores o de implementación cruzada, lo que realmente significa que debe evitarse por completo.

Probablemente esta no sea una respuesta popular, pero es casi seguro que no quieras hacerlo.Si desea una copia que sea una combinación, utilice copiar (o copia profunda, dependiendo de lo que quieras) y luego actualizar.Las dos líneas de código son mucho más legibles (más pitónicas) que la creación de una sola línea con .items() + .items().Lo explícito es mejor que lo implícito.

Además, cuando usas .items() (anterior a Python 3.0), estás creando una nueva lista que contiene los elementos del dict.Si sus diccionarios son grandes, entonces eso supone una gran sobrecarga (dos listas grandes que se desecharán tan pronto como se cree el dictado fusionado).update() puede funcionar de manera más eficiente, porque puede ejecutar el segundo dictado elemento por elemento.

En términos de tiempo:

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

En mi opinión, la pequeña desaceleración entre los dos primeros vale la pena por la legibilidad.Además, los argumentos de palabras clave para la creación de diccionarios solo se agregaron en Python 2.3, mientras que copy() y update() funcionarán en versiones anteriores.

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 (una x86_64 bastante normal con Python 2.5.2), alternativa z2 no sólo es más corto y sencillo, sino también mucho más rápido.Puedes verificar esto por ti mismo utilizando el timeit módulo que viene con Python.

Ejemplo 1:diccionarios idénticos que asignan 20 números 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 aproximadamente.Diferentes diccionarios parecen arrojar resultados bastante diferentes, pero z2 Siempre parece salir adelante.(Si obtiene resultados inconsistentes para el mismo prueba, intenta pasar -r con un número mayor que el predeterminado 3.)

Ejemplo 2:Diccionarios no superpuestos que asignan 252 cadenas cortas a números 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 por aproximadamente un factor de 10.¡Esa es una gran victoria en mi opinión!

Después de comparar esos dos, me pregunté si z1El bajo rendimiento de podría atribuirse a la sobrecarga de construir 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, p.

% 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 lleva a concluir que z3 es algo más rápido que z1, pero no tan rápido como z2.Definitivamente no vale la pena escribir tanto.

A esta discusión todavía le falta algo importante, que es una comparación del rendimiento de estas alternativas con la forma "obvia" de fusionar dos listas:utilizando el update método.Para tratar de mantener las cosas en igualdad de condiciones con las expresiones, ninguna de las cuales modifica x o y, voy a hacer 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?No....

De hecho, me atrevería a afirmar que es imposible que el código Python puro funcione mejor que esto.Y si puede hacerlo significativamente mejor en un módulo de extensión C, imagino que la gente de Python podría estar interesada en incorporar su código (o una variación de su enfoque) en el núcleo de Python.Usos de Python 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 (como era de esperar) la diferencia en notación resulta no tener ningún efecto mensurable en el rendimiento.Utilice el que le parezca más adecuado.Por supuesto, tiene toda la razón al señalar que la versión de dos declaraciones es mucho más fácil de entender.

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

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

En Python 3, puedes usar colecciones.ChainMap que agrupa varios dictados 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

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 por las ediciones.

La mejor versión que se me ocurre sin usar la copia sería:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

es más rápido que dict(x.items() + y.items()) pero no tan rápido como n = copy(a); n.update(b), al menos en CPython.Esta versión también funciona en Python 3 si cambia iteritems() a items(), que lo realiza automáticamente la herramienta 2to3.

Personalmente, me gusta más esta versión porque describe bastante bien lo que quiero en una única sintaxis funcional.El único problema menor es que no hace completamente obvio que los valores de y tengan prioridad sobre los valores de x, pero no creo que sea difícil entenderlo.

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

Para elementos con claves en ambos diccionarios ('b'), puede controlar cuál termina en la salida colocándolo al final.

Python 3.5 (PEP 448) permite una opción de sintaxis más agradable:

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}

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

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 malvado z2 mencionado anteriormente, pero fácil de entender y cambiar.

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

new = dict_merge(old, extras)

Entre respuestas tan turbias y dudosas, este brillante ejemplo es la única buena manera de fusionar dictados en Python, respaldada por un dictador de por vida. Guido van Rossum ¡él mismo!Alguien más sugirió la mitad de esto, pero no lo incluyó en una función.

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

da:

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

Si crees que las lambdas son malas, 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 probablemente sea una mejor manera de hacerlo.

Sea pitónico.Usar una comprensión:

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

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

En Python3, el items método ya no devuelve una lista, sino más bien un vista, que actúa como un conjunto.En este caso necesitarás tomar la unión establecida ya que concatenas con + no funcionará:

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

Para un comportamiento similar al de Python3 en la versión 2.7, el viewitems El método debería funcionar en lugar de items:

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

Prefiero esta notación de todos modos ya que parece más natural pensar en ella como una operación de unión de conjuntos en lugar de una concatenación (como muestra el título).

Editar:

Un par de puntos más para Python 3.Primero, tenga en cuenta que el dict(x, **y) El truco no funcionará en Python 3 a menos que las claves en y son cuerdas.

Además, el mapa de cadenas de Raymond Hettinger respuesta es bastante elegante, ya que puede tomar un número arbitrario de dictados como argumentos, pero de los documentos parece que revisa secuencialmente una lista de todos los dicts para cada búsqueda:

Las búsquedas buscan las asignaciones subyacentes sucesivamente hasta que se encuentra una clave.

Esto puede ralentizarlo si tiene muchas búsquedas en su aplicación:

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

Entonces, aproximadamente un orden de magnitud más lento para las búsquedas.Soy fanático de Chainmap, pero parece menos práctico donde puede haber muchas búsquedas.

Abuso que conduce a una solución de una sola expresión para La respuesta de Mateo.:

>>> 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é lambda para vincular un nombre y tuplas para anular el límite de una expresión de lambda.Siéntete libre de avergonzarte.

Por supuesto, también puedes hacer esto si no te interesa copiarlo:

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

Solución simple que utiliza itertools que preserva el orden (los últimos dictados tienen prioridad)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

Y su uso:

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

Dos diccionarios

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

norte diccionarios

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum tiene mal desempeño.Ver https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

Aunque las respuestas fueron buenas para esto. poco profundo diccionario, ninguno de los métodos definidos aquí realmente realiza una combinación profunda del diccionario.

A continuación se muestran ejemplos:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

Uno esperaría un resultado de algo como esto:

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

En cambio, obtenemos esto:

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

La entrada 'uno' debería haber tenido 'profundidad_2' y 'extra' como elementos dentro de su diccionario si realmente fue una combinación.

Usar cadena tampoco funciona:

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

Resultados en:

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

La fusión profunda que dio rcwesick también crea el mismo resultado.

Sí, funcionará para fusionar los diccionarios de muestra, pero ninguno de ellos es un mecanismo genérico para fusionar.Actualizaré esto más tarde una vez que escriba un método que realice una fusión verdadera.

Basándome en 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 una lambda en su lugar.

El problema que tengo con las soluciones enumeradas hasta la fecha es que, en el diccionario combinado, el valor de la clave "b" es 10 pero, en mi opinión, debería ser 12.En ese sentido, presento lo siguiente:

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

Resultados:

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}
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}

Esto se puede hacer con una sola comprensión de 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)
    }

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

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.

(Solo para Python2.7*;existen 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 a poco en el lambda es necesario porque dict.update siempre regresa None sobre el éxito.)

Es tan tonto que .update no devuelve nada.
Solo uso una función auxiliar simple para resolver el problema:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

Ejemplos:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

Usando una comprensión de dictado, puedes

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

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

da

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

Tenga en cuenta la sintaxis para if else en comprension

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

Sé que esto realmente no se ajusta a los detalles de las preguntas ("one liner"), pero dado que ninguno de las respuestas anteriores iban en esta dirección, aunque muchísimas respuestas abordaban el problema del rendimiento, sentí que debía contribuir con mis ideas.

Dependiendo del caso de uso, puede que no sea necesario crear un diccionario combinado "real" de los diccionarios de entrada proporcionados.A vista que hace esto podría ser suficiente en muchos casos, i.mi.un objeto que actúa como el diccionario fusionado lo haría sin computarlo por completo.Una versión perezosa del diccionario fusionado, por así decirlo.

En Python, esto es bastante simple y se puede hacer con el código que se muestra al final de mi publicación.Dado esto, la respuesta a la pregunta original sería:

z = MergeDict(x, y)

Al utilizar este nuevo objeto, se comportará como un diccionario fusionado, pero tendrá un tiempo de creación constante y una huella de memoria constante, sin modificar los diccionarios originales.Crearlo es mucho más económico que las otras soluciones propuestas.

Por supuesto, si usa mucho el resultado, en algún momento llegará al límite en el que crear un diccionario fusionado real habría sido la solución más rápida.Como dije, depende de su caso de uso.

Si alguna vez sentiste que preferirías tener una fusión real dict, luego llamando dict(z) lo produciría (pero mucho más costoso que las otras soluciones, por supuesto, por lo que vale la pena mencionarlo).

También puedes usar esta clase para crear una especie de diccionario de copia en escritura:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

Aquí está el código sencillo de MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top