Tengo dos diccionarios Python, y quiero escribir una sola expresión que devuelva estos dos diccionarios, combinados. El update()
método sería lo que necesito, si devuelve su resultado en lugar de modificar un diccionario 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 diccionario combinado final z
, no x
?
(Para ser más claro, el manejo del conflicto de último ganador dict.update()
es lo que también estoy buscando).
python
dictionary
merge
Carl Meyer
fuente
fuente
z = x | y
Respuestas:
Para los diccionarios
x
yy
, sez
convierte en un diccionario poco fusionado con valores quey
reemplazan a los dex
.En Python 3.5 o superior:
En Python 2, (o 3.4 o inferior) escriba una función:
y ahora:
En Python 3.9.0a4 o superior (fecha de lanzamiento final aproximadamente en octubre de 2020): PEP-584 , discutido aquí , se implementó para simplificar aún más esto:
Explicación
Digamos que tiene dos dictados y desea fusionarlos en un nuevo dict sin alterar los dictados originales:
El resultado deseado es obtener un nuevo diccionario (
z
) con los valores combinados, y los valores del segundo dict sobrescribiendo los del primero.Una nueva sintaxis para esto, propuesta en PEP 448 y disponible a partir de Python 3.5 , es
Y de hecho es una sola expresión.
Tenga en cuenta que también podemos fusionarnos con la notación literal:
y ahora:
Ahora se muestra como implementado en el cronograma de lanzamiento de 3.5, PEP 478 , y ahora ha llegado al documento Novedades en Python 3.5 .
Sin embargo, dado que muchas organizaciones todavía están en Python 2, es posible que desee hacer esto de una manera 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:
En ambos enfoques,
y
vendrá en segundo lugar y sus valores reemplazarán ax
los valores, por'b'
lo tanto, apuntarán a3
nuestro resultado final.Todavía no en Python 3.5, pero quiere una sola expresión
Si aún no está en Python 3.5, o necesita escribir código compatible con versiones anteriores, y desea esto en una sola expresión , el enfoque más eficaz y correcto es ponerlo en una función:
y luego tienes una sola expresión:
También puede hacer una función para fusionar un número indeterminado de dictados, de cero a un número muy grande:
Esta función funcionará en Python 2 y 3 para todos los dictados. ej. dados dados
a
ag
:y los pares de valores clave
g
tendrán prioridad sobre los dictadosa
af
, y así sucesivamente.Críticas de otras respuestas
No use lo que ve en la respuesta anteriormente aceptada:
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á agregando dos
dict_items
objetos juntos, no dos listas:y tendrías 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álculo.Del mismo modo, tomar la unión de
items()
Python 3 (viewitems()
en Python 2.7) también fallará cuando los valores sean objetos no compartibles (como listas, por ejemplo). Incluso si sus valores son hashable, ya que los conjuntos están semánticamente desordenados, el comportamiento no está definido con respecto a la precedencia. Entonces no hagas esto:Este ejemplo demuestra lo que sucede cuando los valores no son compartibles:
Aquí hay un ejemplo donde y debería tener prioridad, pero en cambio el valor de x se retiene debido al orden arbitrario de los conjuntos:
Otro truco que no debes usar:
Esto usa el
dict
constructor, y es muy rápido y eficiente en la 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 dict constructor), es difícil de leer, no es el uso previsto, por lo que no es Pythonic.Aquí hay un ejemplo del uso que se está remediando en django .
Los dictos están destinados a tomar claves hashables (por ejemplo, congelados o tuplas), pero este método falla en Python 3 cuando las claves no son cadenas.
De la lista de correo , Guido van Rossum, el creador del lenguaje, escribió:
y
Es mi entendimiento (así como el entendimiento del creador del lenguaje ) que el uso previsto
dict(**y)
es para crear dictados con fines de legibilidad, por ejemplo:en vez de
Respuesta a comentarios
Nuevamente, no funciona para 3 cuando las teclas 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 son cadenas. Todas las otras callables lo hicieron cumplir.
dict
rompió esta consistencia en Python 2:Esta inconsistencia fue mala dada otras implementaciones de Python (Pypy, Jython, IronPython). Por lo tanto, se solucionó en Python 3, ya que este uso podría ser un cambio radical.
Le presento que es una incompetencia maliciosa escribir código intencionalmente que solo funciona en una versión de un idioma o que solo funciona dadas ciertas restricciones arbitrarias.
Más comentarios:
Mi respuesta: en
merge_two_dicts(x, y)
realidad me parece mucho más claro, si realmente nos preocupa la legibilidad. Y no es compatible con versiones anteriores, ya que Python 2 está cada vez más obsoleto.Si. Debo remitirlo nuevamente a la pregunta, que es solicitar una fusión superficial de dos diccionarios, con los valores del primero sobrescritos por los segundos, 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 evitar eso es hacer una copia al asignar valores. Como las claves deben ser hashable y, por lo tanto, generalmente son inmutables, no tiene sentido copiarlas:
Uso:
Proponer contingencias para otros tipos de valores está mucho más allá del alcance de esta pregunta, por lo que le diré mi respuesta a la pregunta canónica sobre una "Fusión de diccionarios de diccionarios" .
Ad-hocs menos efectivos pero correctos
Estos enfoques son menos efectivos, pero proporcionarán un comportamiento correcto. Tendrán mucho menos rendimiento 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 lo hacen respetar el orden de precedencia (este último predice tienen prioridad)También puede encadenar los dictados manualmente dentro de una comprensión dict:
o en python 2.6 (y tal vez tan pronto como 2.4 cuando se introdujeron las expresiones generadoras):
itertools.chain
encadenará los iteradores sobre los pares clave-valor en el orden correcto:Análisis de rendimiento
Solo voy a hacer el análisis de rendimiento de los usos que se sabe que se comportan correctamente.
Lo siguiente se hace en Ubuntu 14.04
En Python 2.7 (sistema Python):
En Python 3.5 (PPA de DenaSnakes):
Recursos sobre diccionarios
fuente
{**{(0, 1):2}}
->{(0, 1): 2}
z = {**x, **y}
realmente me estimulanx | y
En su caso, lo que puede hacer es:
Esto, como lo desee, colocará el dict final
z
y hará que el valor de la claveb
sea anulado correctamente por el valor del segundo (y
) dict:Si usa Python 3, es solo un poco más complicado. Para crear
z
:Si usa Python versión 3.9.0a4 o superior, puede usar directamente:
fuente
Una alternativa:
fuente
Update
No es una de las funciones "centrales" que las personas tienden a usar mucho.(lambda z: z.update(y) or z)(x.copy())
: POtra opción más concisa:
Nota : esta se ha convertido en una respuesta popular, pero es importante señalar que si
y
tiene alguna clave que no sea de cadena, el hecho de que esto funcione es un abuso de los detalles de implementación de CPython, y no funciona en Python 3, o en PyPy, IronPython o Jython. Además, Guido no es fanático . Por lo tanto, no puedo recomendar esta técnica para el código portátil compatible con versiones posteriores o de implementación cruzada, lo que realmente significa que debe evitarse por completo.fuente
Probablemente esta no sea una respuesta popular, pero es casi seguro que no desea hacerlo. Si desea una copia que sea una fusión, use copy (o deepcopy , según lo que desee) 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 () (pre 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 grandes listas que se desecharán tan pronto como se cree el dict combinado). update () puede funcionar de manera más eficiente, ya que puede ejecutarse a través del segundo dict ítem por ítem.
En términos de tiempo :
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.
fuente
En una respuesta de seguimiento, usted preguntó sobre el rendimiento relativo de estas dos alternativas:
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 también significativamente más rápida. Puedes verificar esto por ti mismo usando eltimeit
módulo que viene con Python.Ejemplo 1: diccionarios idénticos que asignan 20 enteros consecutivos a sí mismos:
z2
gana por un factor de 3.5 más o menos. Los diferentes diccionarios parecen arrojar resultados bastante diferentes, peroz2
siempre parecen salir adelante. (Si obtiene resultados inconsistentes para la misma prueba, intente pasar-r
con un número mayor que el predeterminado 3.)Ejemplo 2: diccionarios no superpuestos que asignan 252 cadenas cortas a enteros y viceversa:
z2
gana por un factor de 10. ¡Esa es una gran victoria en mi libro!Después de comparar esos dos, me preguntaba si
z1
el bajo rendimiento 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:Algunas pruebas rápidas, p. Ej.
me llevó a concluir que
z3
es algo más rápido quez1
, pero no tan rápido comoz2
. Definitivamente no vale la pena todo el tipeo adicional.A esta discusión todavía le falta algo importante, que es una comparación de rendimiento de estas alternativas con la forma "obvia" de fusionar dos listas: usar 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 su lugar, de la siguiente manera:Un resultado típico:
En otras palabras,
z0
yz2
parecen tener un rendimiento esencialmente idéntico. ¿Crees que esto podría ser una coincidencia? Yo no....De hecho, iría tan lejos como para afirmar que es imposible para el código Python puro hacer algo 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. Python utiliza
dict
en muchos lugares; optimizar sus operaciones es un gran problema.También podrías escribir esto como
como lo hace Tony, pero (no es sorprendente) que la diferencia en notación no tenga ningún efecto medible en el rendimiento. Use lo que le parezca más adecuado. Por supuesto, tiene toda la razón al señalar que la versión de dos afirmaciones es mucho más fácil de entender.
fuente
items()
no es catenable yiteritems
no existe.En Python 3.0 y
collections.ChainMap
versiones posteriores , puede usar qué grupos múltiples dictados u otras asignaciones juntas para crear una vista única y actualizable:Actualización para Python 3.5 y posterior : puede usar el empaquetado y desempaquetado extendido del diccionario PEP 448 . Esto es rápido y fácil:
fuente
del
say, ChainMap c eliminará la primera asignación de esa clave.dict
evitar eso, es decir:dict(ChainMap({}, y, x))
Quería algo similar, pero con la capacidad de especificar cómo se fusionaron los valores en las claves duplicadas, así que lo pirateé (pero no lo probé en gran medida). Obviamente, esta no es una sola expresión, pero es una llamada de función única.
fuente
Actualización recursiva / profunda de un dict
Demostración:
Salidas:
Gracias rednaw por ediciones.
fuente
La mejor versión que podría pensar mientras no usa copia sería:
Es más rápido
dict(x.items() + y.items())
pero no tan rápido comon = copy(a); n.update(b)
, al menos en CPython. Esta versión también funciona en Python 3 si cambiaiteritems()
aitems()
, que 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 única sintaxis funcional. El único problema menor es que no es completamente obvio que los valores de y tienen prioridad sobre los valores de x, pero no creo que sea difícil de resolver.
fuente
Python 3.5 (PEP 448) permite una mejor opción de sintaxis:
O incluso
En Python 3.9 también usas | y | = con el siguiente ejemplo de PEP 584
fuente
dict(x, **y)
solución? Como usted (@CarlMeyer) mencionó en la nota de su propia respuesta ( stackoverflow.com/a/39858/2798610 ) Guido considera que esa solución es ilegal .dict(x, **y)
la razón (muy buena) de que se basa eny
tener solo claves que son nombres válidos de argumentos de palabras clave (a menos que esté utilizando CPython 2.7, donde el constructor dict engaña). Esta objeción / restricción no se aplica a PEP 448, que generaliza la**
sintaxis de desempaquetado para dictar literales. Entonces, esta solución tiene la misma concisión quedict(x, **y)
, sin la desventaja.Para los elementos con claves en ambos diccionarios ('b'), puede controlar cuál termina en la salida colocando ese último.
fuente
Si bien la pregunta ya ha sido respondida varias veces, esta solución simple al problema aún no se ha enumerado.
Es tan rápido como z0 y el malvado z2 mencionado anteriormente, pero fácil de entender y cambiar.
fuente
z4 = {}
y cambie la siguiente línea az4 = x.copy()
: mejor que un buen código no hace cosas innecesarias (lo que lo hace aún más legible y mantenible).Entre estas respuestas dudosas y dudosas, este brillante ejemplo es la única y única buena manera de fusionar los dictados en Python, ¡respaldado por el dictador de por vida Guido van Rossum ! Alguien más sugirió la mitad de esto, pero no lo puso en una función.
da:
fuente
Si crees que las lambdas son malvadas, no sigas leyendo. Según lo solicitado, puede escribir la solución rápida y eficiente en la memoria con una expresión:
Como se sugirió anteriormente, usar dos líneas o escribir una función es probablemente una mejor manera de hacerlo.
fuente
Sé pitónico. Usa una comprensión :
fuente
def dictmerge(*args): return {i:d[i] for d in args for i in d}
z={k: v for d in (x, y) for k, v in d.items()}
En python3, el
items
método ya no devuelve una lista , sino más bien una vista , que actúa como un conjunto. En este caso, deberá tomar la unión de conjunto, ya que la concatenación con+
no funcionará:Para el comportamiento similar a python3 en la versión 2.7, el
viewitems
método debería funcionar en lugar deitems
:De todos modos, prefiero esta notación, ya que parece más natural pensar en ella como una operación de unión de conjuntos que como 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)
truco no funcionará en Python 3 a menos que las claves eny
sean cadenas.Además, la respuesta de Raymond Hettinger en Chainmap es bastante elegante, ya que puede tomar una cantidad arbitraria de dictados como argumentos, pero de los documentos parece que busca secuencialmente una lista de todos los dictados para cada búsqueda:
Esto puede ralentizarlo si tiene muchas búsquedas en su aplicación:
Entonces, 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.
fuente
Abuso que conduce a una solución de una sola expresión para la respuesta de Matthew :
Dijiste que querías una expresión, así que abusé
lambda
de unir un nombre y tuplas para anular el límite de una expresión de lambda. Siéntase libre de encogerse.También podría hacer esto, por supuesto, si no le importa copiarlo:
fuente
Solución simple usando herramientas iterativas que preserva el orden (los últimos dictados tienen prioridad)
Y es uso:
fuente
Dos diccionarios
n diccionarios
sum
Tiene mal rendimiento. Ver https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/fuente
Aunque las respuestas fueron buenas para este diccionario superficial , ninguno de los métodos definidos aquí realmente hace una fusión profunda de diccionario.
Siguen ejemplos:
Uno esperaría un resultado de algo como esto:
En cambio, obtenemos esto:
La entrada 'one' debería haber tenido 'depth_2' y 'extra' como elementos dentro de su diccionario si realmente fuera una fusión.
Usar cadena también no funciona:
Resultados en:
La profunda fusión que rcwesick dio también crea el mismo resultado.
Sí, funcionará fusionar los diccionarios de muestra, pero ninguno de ellos es un mecanismo genérico para fusionar. Actualizaré esto más adelante una vez que escriba un método que haga una verdadera fusión.
fuente
(Solo para Python2.7 *; existen soluciones más simples para Python3 *.)
Si no eres reacio a importar un módulo de biblioteca estándar, puedes hacer
(El
or a
bit en ellambda
es necesario porquedict.update
siempre regresaNone
al éxito).fuente
Si no te importa mutar
x
,Simple, legible, performante. Usted sabe
update()
siempre vuelveNone
, que es un valor falso. Entonces la expresión anterior siempre evaluará ax
, después de actualizarla.Los métodos de mutación en la biblioteca estándar (como
.update()
) regresanNone
por convención, por lo que este patrón también funcionará en ellos. Si está utilizando un método que no sigue esta convención, esor
posible que no funcione. Pero, en su lugar, puede usar una visualización e índice de tuplas para convertirlo en una sola expresión. Esto funciona independientemente de lo que evalúa el primer elemento.Si aún no tiene
x
una variable, puede usarlalambda
para hacer un local sin usar una declaración de asignación. Esto equivale a usarlambda
como una expresión let , que es una técnica común en lenguajes funcionales, pero tal vez no es pitónica.Aunque no es tan diferente del siguiente uso del nuevo operador de morsa (solo Python 3.8+):
Si desea una copia, el estilo PEP 448 es el más fácil
{**x, **y}
. Pero si eso no está disponible en su versión de Python (anterior), el patrón let también funciona aquí.(Eso es, por supuesto, equivalente a
(z := x.copy()).update(y) or z
, pero si su versión de Python es lo suficientemente nueva para eso, entonces el estilo PEP 448 estará disponible).fuente
Basándome en ideas aquí y en otros lugares, he comprendido una función:
Uso (probado en python 3):
Podrías usar una lambda en su lugar.
fuente
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, a mi modo de ver, debería ser 12. En ese sentido, presento lo siguiente:
Resultados:
fuente
cytoolz.merge_with
( toolz.readthedocs.io/en/latest/… )Es tan tonto que
.update
no devuelve nada.Solo uso una función auxiliar simple para resolver el problema:
Ejemplos:
fuente
Esto debería solucionar tu problema.
fuente
Esto se puede hacer con una sola comprensión dict:
En mi opinión, la mejor respuesta para la parte de 'expresión única' ya que no se necesitan funciones adicionales, y es breve.
fuente
Habrá una nueva opción cuando se publique Python 3.8 ( programado para el 20 de octubre de 2019 ), gracias a PEP 572: Expresiones de asignación . El nuevo operador de expresión de asignación le
:=
permite asignar el resultado delcopy
y aún usarlo para llamarupdate
, dejando al código combinado una sola expresión, en lugar de dos declaraciones, cambiando:a:
mientras se comporta de manera idéntica en todos los sentidos. Si también debe devolver el resultado
dict
(solicitó una expresión que devuelva eldict
; lo anterior crea y asigna anewdict
, pero no lo devuelve, por lo que no podría usarlo para pasar un argumento a una función como es, a lamyfunc((newdict := dict1.copy()).update(dict2))
) , luego solo agregueor newdict
al final (dado queupdate
devuelveNone
, lo cual es falso, luego evaluará y regresaránewdict
como resultado de la expresión):Advertencia importante: en general, desalentaría este enfoque a favor de:
El enfoque de desempaquetado es más claro (para cualquier persona que conozca el desempaque generalizado en primer lugar, lo que debería hacer ), no requiere un nombre para el resultado en absoluto (por lo que es mucho más conciso al construir un temporal que se pasa inmediatamente a un función o incluido en un
list
/tuple
literal o similar), y es casi seguro también más rápido, siendo (en CPython) aproximadamente equivalente a:pero se realiza en la capa C, utilizando la
dict
API concreta , por lo que no se trata de una búsqueda / vinculación de método dinámico o sobrecarga de despacho de llamadas de función (donde(newdict := dict1.copy()).update(dict2)
es inevitablemente idéntico al comportamiento original de dos líneas, realizando el trabajo en pasos discretos, con búsqueda dinámica / enlace / invocación de métodos.También es más extensible, ya que la fusión de tres
dict
s es obvia:donde el uso de expresiones de asignación no se escalará así; lo más cerca que podrías estar sería:
o sin la tupla temporal de
None
s, pero con pruebas de veracidad de cadaNone
resultado:cualquiera de los cuales es, obviamente, mucho más feo, y además incluye las ineficiencias (ya sea un desperdicio temporal
tuple
deNone
s para la separación de coma, o truthiness inútil prueba de cadaupdate
'sNone
cambio deor
separación).La única ventaja real del enfoque de expresión de asignación ocurre si:
set
s comodict
s (ambos admitencopy
yupdate
, por lo tanto, el código funciona más o menos como lo esperaría)dict
sí mismo, y debe preservar el tipo y la semántica del lado izquierdo (en lugar de terminar con un planodict
). Si bienmyspecialdict({**speciala, **specialb})
podría funcionar, implicaría una temporalidad adicionaldict
, y simyspecialdict
tiene características simplesdict
, no se puede conservar (por ejemplo,dict
ahora los s normales conservan el orden en función de la primera aparición de una clave y el valor en función de la última aparición de una clave; es posible que desee uno que conserva el orden en función de la últimoapariencia de una clave, por lo que actualizar un valor también lo mueve al final), entonces la semántica sería incorrecta. Dado que la versión de expresión de asignación usa los métodos nombrados (que presumiblemente están sobrecargados para comportarse adecuadamente), nunca crea undict
en absoluto (a menos quedict1
ya fuera adict
), preservando el tipo original (y la semántica del tipo original), todo mientras se evitan los temporales.fuente
fuente
x
con su copia. Six
es un argumento de función, esto no funcionará (ver ejemplo )