¿Cómo combino dos diccionarios en una sola expresión?

8 minutos de lectura

avatar de usuario
carl meyer

Quiero fusionar dos diccionarios en un nuevo diccionario.

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = merge(x, y)

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

Siempre que una llave k está presente en ambos diccionarios, sólo el valor x[k] debería ser guardado.

avatar de usuario
Thomas Vander Stichele

En tu caso, lo que puedes hacer es:

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

Esto, como lo desee, pondrá el dictado final en zy hacer el valor de clave b ser anulado correctamente por el segundo (y) valor de dictado:

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

Si usa Python 2, incluso puede eliminar el list() llamadas Para crear z:

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

Si usa Python versión 3.9.0a4 o superior, puede usar directamente:

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

  • No use esto ya que es muy ineficiente. (Vea los resultados de timeit a continuación). Puede haber sido necesario en los días de Py2 si una función de contenedor no era una opción, pero esos días ya pasaron.

    – Gringo Suave

    11 de marzo de 2020 a las 17:22


  • Para aclarar por qué esto no cumple con los criterios proporcionados por la pregunta: no es una sola expresión y no devuelve z.

    – Alejandro Ah

    21 de marzo de 2013 a las 13:15


  • Póngalo de esta manera: si necesita poner dos líneas de comentarios que expliquen su única línea de código a las personas a las que le pasa el código… ¿realmente lo ha hecho en una sola línea? 🙂 Estoy totalmente de acuerdo en que Python no es bueno para esto: debería haber una manera mucho más fácil. Si bien esta respuesta es más pitónica, ¿es realmente tan explícita o clara? Update no es una de las funciones “básicas” que la gente tiende a usar mucho.

    -Eric

    19 de octubre de 2017 a las 13:07


  • Bueno, si la gente insiste en convertirlo en una sola línea, siempre puedes hacerlo. (lambda z: z.update(y) or z)(x.copy()) :PAGS

    – torre

    24 de febrero de 2020 a las 12:43

avatar de usuario
carl meyer

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 un fan. Por lo tanto, no puedo recomendar esta técnica para código portátil de implementación cruzada o compatible con versiones posteriores, lo que realmente significa que debe evitarse por completo.

  • Funciona bien en Python 3 y PyPy y PyPy 3, no puede hablar con Jython o Iron. Dado este patrón es explícitamente documentado (consulte el tercer formulario de constructor en esta documentación) Yo diría que no es un “detalle de implementación” sino un uso intencional de la función.

    – amcgregor

    12 de abril de 2019 a las 13:10

  • @amcgregor Te perdiste la frase clave “si y tiene alguna clave que no sea una cadena”. Eso es lo que no funciona en Python3; el hecho de que funcione en CPython 2 es un detalle de implementación en el que no se puede confiar. IFF garantiza que todas sus claves sean cadenas, esta es una opción totalmente compatible.

    –Carl Meyer

    10 mayo 2019 a las 16:27

avatar de usuario
twasbrillig

Probablemente esta no sea una respuesta popular, pero es casi seguro que no quieras hacer esto. Si desea una copia que sea una combinación, use copiar (o copia profunda, dependiendo de 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() (anterior a Python 3.0), está creando una nueva lista que contiene los elementos del dict. Si sus diccionarios son grandes, entonces eso es una gran sobrecarga (dos listas grandes que se descartarán tan pronto como se cree el dict fusionado). update() puede funcionar de manera más eficiente, ya que 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.

  • Funciona bien en Python 3 y PyPy y PyPy 3, no puede hablar con Jython o Iron. Dado este patrón es explícitamente documentado (consulte el tercer formulario de constructor en esta documentación) Yo diría que no es un “detalle de implementación” sino un uso intencional de la función.

    – amcgregor

    12 de abril de 2019 a las 13:10

  • @amcgregor Te perdiste la frase clave “si y tiene alguna clave que no sea una cadena”. Eso es lo que no funciona en Python3; el hecho de que funcione en CPython 2 es un detalle de implementación en el que no se puede confiar. IFF garantiza que todas sus claves sean cadenas, esta es una opción totalmente compatible.

    –Carl Meyer

    10 mayo 2019 a las 16:27

avatar de usuario
el hombre de hojalata

En una respuesta de seguimiento, preguntó sobre el rendimiento 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 ordinaria que ejecuta Python 2.5.2), alternativa z2 no solo es más corto y simple, sino también significativamente más rápido. Puede comprobarlo usted mismo utilizando el timeit módulo que viene con Python.

Ejemplo 1: diccionarios idénticos que asignan 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 más o menos. 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 un factor de 10. ¡Esa es una gran victoria en mi opinión!

Después de comparar esos dos, me preguntaba 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, 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 lleva a concluir que z3 es algo más rápido que z1pero no tan rápido como z2. Definitivamente no vale la pena todo el tipeo extra.

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 pie de igualdad 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? Yo no….

De hecho, iría tan lejos como para afirmar que es imposible que el código puro de Python funcione mejor que esto. Y si puede hacerlo significativamente mejor en un módulo de extensión de C, me 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 pitón 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 la notación resulta no tener ningún efecto medible en el rendimiento. Usa lo que te parezca correcto. 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.

  • Esto no funciona en Python 3; items() no es catenable, y iteritems no existe.

    – Antti Haapala — Слава Україні

    16 de marzo de 2015 a las 5:50

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad