[python] ¿Cómo combinar dos diccionarios en una sola expresión?


Answers

En tu caso, lo que puedes hacer es:

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

Esto, como lo desee, ponga el dict final en z , y haga que el valor de la clave b sea ​​anulado correctamente por el valor del segundo ( y ) 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 usas Python 3, es solo un poco más complicado. Para crear z :

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

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

>>> 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 dict fusionado final en z , no en x ?

(Para ser extra claro, el manejo de conflictos last-one-wins de dict.update() es lo que estoy buscando también).




Otra opción más concisa:

z = dict(x, **y)

Nota : esto 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 los detalles de implementación de CPython, y no funciona en Python 3 , o en PyPy, IronPython o Jython. Además, Guido no es un fanático . Por lo tanto, no puedo recomendar esta técnica para el código portátil compatible o compatible con versiones posteriores, lo que realmente significa que debe evitarse por completo.




Using a dict comprehension, you may

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}

Note the syntax for if else in comprehension

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



En python3, el método de items ya no devuelve una lista , sino una vista , que actúa como un conjunto. En este caso, deberá 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 los 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 establecida 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 truco dict(x, **y) no funcionará en python 3 a menos que las claves en y sean cadenas.

Además, la answer mapa de cadenas de Raymond Hettinger es bastante elegante, ya que puede tomar un número arbitrario de dictados como argumentos, pero de los documentos parece que se ve secuencialmente a través de una lista de todos los dictados de cada búsqueda:

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

Esto puede ralentizarte si tienes muchas búsquedas en tu 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, un orden de magnitud más lento para las búsquedas. Soy un fanático de Chainmap, pero parece menos práctico donde puede haber muchas búsquedas.




En Python 3, puede usar collections.ChainMap que agrupa múltiples 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



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



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 al poner esa última.







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

new = dict_merge(old, extras)

Entre esas respuestas turbias y dudosas, este brillante ejemplo es la única y única buena manera de fusionar dictados en Python, respaldado por el dictador de por vida Guido van Rossum mismo. 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'}



El problema que tengo con las soluciones enumeradas hasta la fecha es que, en el diccionario fusionado, el valor para la clave "b" es 10 pero, a mi modo de ver, 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}






Abuso que conduce a una solución de una 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 enlazar un nombre, y tuplas para anular el límite de una expresión de lambda. Siéntase libre de encogerse.

También puedes hacer esto por supuesto si no te importa copiarlo:

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



En 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 (un x86_64 bastante ordinario 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. Puede verificar esto usted mismo usando el módulo timeit que viene con Python.

Ejemplo 1: diccionarios idénticos que mapean 20 enteros consecutivos consigo 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. Distintos diccionarios parecen arrojar 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 predeterminado 3.)

Ejemplo 2: diccionarios no superpuestos que mapean 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 todavía le falta algo importante, que es una comparación del rendimiento de estas alternativas con la forma "obvia" de fusionar dos listas: usar 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, 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 para el código Python puro hacer algo mejor que esto. Y si puede hacer mucho mejor en un módulo de extensión C, me imagino que la gente de Python podría estar interesada en incorporar su código (o una variación en su enfoque) en el núcleo de Python. Python usa dict en muchos lugares; optimizar sus operaciones es un gran problema.

También puedes escribir esto como

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

como Tony lo hace, pero (como es lógico) la diferencia en la notación resulta no tener ningún efecto mensurable en el rendimiento. Use el que le 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.




La mejor versión que podría pensar 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) 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 hace 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 resulta completamente obvio que los valores de y tengan prioridad sobre los valores de x, pero no creo que sea difícil darse cuenta de eso.




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

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



Related