python - unir - ¿Cómo fusionar dos diccionarios en una sola expresión?
unir dos diccionarios (30)
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
- Mi explicación de la implementación del diccionario de Python, actualizada para 3.6.
- Responde sobre cómo agregar nuevas claves a un diccionario
- Mapeo de dos listas en un diccionario
- Los documentos oficiales de Python en los diccionarios.
- The Dictionary Even Mightier - charla de Brandon Rhodes en Pycon 2017
- Diccionarios modernos de Python, una confluencia de grandes ideas - charla de Raymond Hettinger en Pycon 2017
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.
A pesar de que las respuestas fueron buenas para este diccionario superficial , ninguno de los métodos aquí definidos hace una fusión profunda del diccionario.
Ejemplos a continuación:
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 ''one'' debería haber tenido ''depth_2'' y ''extra'' como elementos dentro de su diccionario si realmente era una combinación.
El uso de la cadena también, no 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 combinar los diccionarios de muestra, pero ninguno de ellos es un mecanismo genérico para combinar. Actualizaré esto más adelante una vez que escriba un método que haga una fusión verdadera.
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}
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}
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 Python 3:
import collections
a = {1: 1, 2: 2}
b = {2: 3, 3: 4}
c = {3: 5}
r = dict(collections.ChainMap(a, b, c))
print(r)
Afuera:
{1: 1, 2: 2, 3: 4}
Docs: https://docs.python.org/3/library/collections.html#collections.ChainMap :
En python3, el método de items
ya no devuelve una lista , sino una vista , que actúa como un conjunto. En este caso, tendrá que tomar la unión establecida ya que la concatenación con +
no funcionará:
dict(x.items() | y.items())
Para el comportamiento similar a python3 en la versión 2.7, el método viewitems
debería funcionar en lugar de items
:
dict(x.viewitems() | y.viewitems())
De todos modos, prefiero esta notación, ya que parece más natural pensar que se trata de una operación sindical establecida en lugar de una concatenación (como lo muestra el título).
Editar:
Un par de puntos más para python 3. En primer lugar, tenga en cuenta que el truco dict(x, **y)
no funcionará en python 3 a menos que las claves en y
sean cadenas.
Además, la answer del mapa de cadenas de Raymond Hettinger es bastante elegante, ya que puede tomar un número arbitrario de dictados como argumentos, pero en los documentos parece que se ve secuencialmente a través de una lista de todos los dictados para cada búsqueda:
Las búsquedas buscan sucesivamente las asignaciones subyacentes 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
Así que alrededor de un orden de magnitud más lento para las búsquedas. Soy un fan de Chainmap, pero parece menos práctico donde puede haber muchas búsquedas.
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.
La mejor versión que podría pensar mientras no uso 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)
n = copy(a); n.update(b)
, al menos en CPython. Esta versión también funciona en Python 3 si cambia iteritems()
a items()
, lo cual se realiza automáticamente con la herramienta 2to3.
Personalmente, me gusta más esta versión porque describe bastante bien lo que quiero en una sola sintaxis funcional. El único problema menor es que no hace completamente obvio que los valores de y tienen prioridad sobre los valores de x, pero no creo que sea difícil descifrarlo.
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}
Probablemente esta no sea una respuesta popular, pero es casi seguro que no quieres hacer esto. Si desea una copia que sea una combinación, entonces use copia (o copia deepcopy , dependiendo de lo que quiera) y luego actualice. Las dos líneas de código son mucho más legibles, más Pythonic, que la creación de una sola línea con .items () + .items (). Explícito es mejor que implícito.
Además, cuando usa .items () (antes de Python 3.0), está creando una nueva lista que contiene los elementos del dict. Si sus diccionarios son grandes, entonces eso es bastante sobrecarga (dos listas grandes que se eliminarán tan pronto como se cree el dictado fusionado). update () puede funcionar de manera más eficiente, ya que puede ejecutarse a través del segundo dictado elemento por elemento.
En términos de 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
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 del diccionario solo se agregaron en Python 2.3, mientras que copy () y update () funcionarán en versiones anteriores.
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
Se pitón. Usa una comprehension :
z={i:d[i] for d in [x,y] for i in d}
>>> print z
{''a'': 1, ''c'': 11, ''b'': 10}
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.
Solución simple utilizando itertools que conserva 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}
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 a
bit en el lambda
es necesario porque dict.update
siempre vuelve None
en éxito).
Dos diccionarios
def union2(dict1, dict2):
return dict(list(dict1.items()) + list(dict2.items()))
n diccionarios
def union(*dicts):
return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))
sum
tiene un mal desempeño. Ver https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/
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}
>>> 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}
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.
x = {''a'':1, ''b'': 2}
y = {''b'':10, ''c'': 11}
z = dict(x.items() + y.items())
print z
Para los elementos con claves en ambos diccionarios (''b''), puede controlar cuál de ellos termina en la salida colocando ese último.