¿Cómo fusiono diccionarios en Python?


91
d3 = dict(d1, **d2)

Entiendo que esto fusiona el diccionario. Pero, ¿es único? ¿Qué pasa si d1 tiene la misma clave que d2 pero un valor diferente? Me gustaría que d1 y d2 se fusionen, pero d1 tiene prioridad si hay una clave duplicada.


9
Tenga en cuenta que este truco se considera un abuso del **paso de argumentos de palabras clave, a menos que todas las claves de d2sean cadenas. Si no todas las claves de d2son cadenas, esto falla en Python 3.2 y en implementaciones alternativas de Python como Jython, IronPython y PyPy. Consulte, por ejemplo, mail.python.org/pipermail/python-dev/2010-April/099459.html .
Mark Dickinson

Respuestas:


154

Puede utilizar el .update()método si ya no necesita el original d2:

Actualice el diccionario con los pares clave / valor de otros, sobrescribiendo las claves existentes . Regreso None.

P.ej:

>>> d1 = {'a': 1, 'b': 2} 
>>> d2 = {'b': 1, 'c': 3}
>>> d2.update(d1)
>>> d2
{'a': 1, 'c': 3, 'b': 2}

Actualizar:

Por supuesto, puede copiar el diccionario primero para crear uno nuevo combinado. Esto puede ser necesario o no. En caso de que tenga objetos compuestos (objetos que contienen otros objetos, como listas o instancias de clase) en su diccionario, copy.deepcopytambién debe considerarse.


1
En este caso, los elementos d1 deberían tener prioridad correctamente si se encuentran claves en conflicto
Trey Hunner

En caso de que aún lo necesite, simplemente haga una copia. d3 = d2.copy () d3.update (d1) pero me gustaría que d1 + d2 se agregue al idioma.
2010

4
d1 + d2 es problemático porque un diccionario debe tener prioridad durante los conflictos y no es particularmente obvio cuál.
rjh

d1 + d2 solo se implementará si Python obtiene un multimapa; de lo contrario, la ambigüedad para el usuario es demasiado confusa para la ganancia de escritura de 8 bytes.
Nick Bastin

Tiene objetos en el diccionario en este ejemplo: isinstance(int, object) is Truepero deepcopyno parece necesario.
Antony Hatchkins

43

En Python2,

d1={'a':1,'b':2}
d2={'a':10,'c':3}

d1 anula d2:

dict(d2,**d1)
# {'a': 1, 'c': 3, 'b': 2}

d2 anula d1:

dict(d1,**d2)
# {'a': 10, 'c': 3, 'b': 2}

Este comportamiento no es solo una casualidad de implementación; está garantizado en la documentación :

Si se especifica una clave tanto en el argumento posicional como en el argumento de palabra clave, el valor asociado con la palabra clave se conserva en el diccionario.


3
Sus ejemplos fallarán (produciendo un TypeError) en Python 3.2, y en las versiones actuales de Jython, PyPy e IronPython: para esas versiones de Python, al pasar un dictado con la **notación, todas las claves de ese dictado deben ser cadenas. Consulte el hilo de python-dev que comienza en mail.python.org/pipermail/python-dev/2010-April/099427.html para obtener más información.
Mark Dickinson

@Mark: Gracias por avisar. Edité el código para hacerlo compatible con implementaciones que no son de CPython.
unutbu

3
falla si sus claves son tuplas de cadenas y números. por ej. d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy

Con respecto a la sintaxis de desempaquetado, consulte esta publicación para conocer los cambios que se realizarán en python 3.5.
Ioannis Filippidis

Iba a decir que d = dict(**d1, **d2)funciona, pero eso es lo que @IoannisFilippidis hace referencia en su comentario. Quizás incluir el fragmento aquí hubiera sido más claro, así que aquí está.
dwanderson

14

Si quiere d1tener prioridad en los conflictos, haga:

d3 = d2.copy()
d3.update(d1)

De lo contrario, invierta d2y d1.


1

Mi solución es definir una función de combinación . No es sofisticado y solo cuesta una línea. Aquí está el código en Python 3.

from functools import reduce
from operator import or_

def merge(*dicts):
    return { k: reduce(lambda d, x: x.get(k, d), dicts, None) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }

Pruebas

>>> d = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> d_letters = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d, d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d_letters, d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> merge(d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge()
{}

Funciona para un número arbitrario de argumentos de diccionario. Si hubiera claves duplicadas en ese diccionario, gana la clave del diccionario situado más a la derecha en la lista de argumentos.


1
Un bucle simple con una .updatellamada en él ( merged={}seguido de for d in dict: merged.update(d)) sería más corto, más legible y más eficiente.
Mark Dickinson

1
O si realmente desea utilizar reducey lambdaS, ¿qué tal return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson

1
Puede probar su código en el shell y ver si es correcto. Lo que estaba tratando de hacer es escribir una función que pueda tomar varios argumentos de diccionario con la misma funcionalidad. Es mejor no usar x.update (y) debajo de lambda, porque siempre devuelve None . Y estoy tratando de escribir una función más general merge_with que tome varios argumentos de diccionario y trate con claves duplicadas con la función proporcionada. Una vez que termine, lo publicaré en otro hilo donde la solución sea más relevante.
Lei Zhao

Aquí está el enlace donde escribí la solución más general. Bienvenido y echa un vistazo.
Lei Zhao


1

Comenzando Python 3.9, el operador |crea un nuevo diccionario con las claves y valores combinados de dos diccionarios:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d3 = d2 | d1
# d3: {'b': 2, 'c': 3, 'a': 1}

Esta:

Crea un nuevo diccionario d3 con las claves y valores combinados de d2 y d1. Los valores de d1 tienen prioridad cuando d2 y d1 comparten claves.


También tenga en cuenta el |=operador que modifica d2 fusionando d1 en, con prioridad en los valores de d1:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d2 |= d1
# d2: {'b': 2, 'c': 3, 'a': 1}


0

Creo que, como se indicó anteriormente, usar d2.update(d1)es el mejor enfoque y que también puede copiar d2primero si aún lo necesita.

Aunque, quiero señalar que en dict(d1, **d2)realidad es una mala manera de fusionar diccionarios en general, ya que los argumentos de palabras clave deben ser cadenas, por lo que fallará si tiene una dictcomo:

{
  1: 'foo',
  2: 'bar'
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.