python unir sumar - ¿Cómo fusionar dos diccionarios en una sola expresión?



15 Answers

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}
update como buscar

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




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.




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.




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



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.




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.




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



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}



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}






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



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.




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}



Related