2010-05-09 23 views
82
d3 = dict(d1, **d2) 

Entiendo que esto combina el diccionario. Pero, ¿es único? ¿Qué pasa si d1 tiene la misma clave que d2 pero diferente valor? Me gustaría que d1 y d2 se fusionaran, pero d1 tiene prioridad si hay una clave duplicada.¿Cómo fusiono diccionarios en Python?

+9

favor tenga en cuenta que este truco se considera un abuso del argumento de palabra clave '**' que pasa a menos que todas las claves de 'd2' sean cadenas. Si no todas las claves de 'd2' son cadenas, esto falla en Python 3.2, y en implementaciones alternativas de Python como Jython, IronPython y PyPy. Ver, por ejemplo, http://mail.python.org/pipermail/python-dev/2010-April/099459.html. –

+1

posible duplicado de [¿Cómo puedo fusionar dos diccionarios de Python en una sola expresión?] (Http://stackoverflow.com/questions/38987/how-can-i-merge-two-python-dictionaries-in-a-single -expresión) –

Respuesta

136

Usted puede utilizar el método .update() si no se necesita el original d2 más:

actualización del diccionario con los pares clave/valor de otros, sobreescritura claves existentes. Vuelva None.

ej .:

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

Actualización:

Por supuesto, puede copiar el primer diccionario con el fin de crear uno nuevo fusionada. Esto puede o no ser necesario. En caso de que tenga objetos compuestos (objetos que contengan otros objetos, como listas o instancias de clases) en su diccionario, también se debe considerar el copy.deepcopy.

+1

Con este caso, los elementos d1 deberían tener prioridad correctamente si se encuentran claves conflictivas –

+0

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

+4

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

36

En python2,

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

d1 d2 Anulaciones: pasa por encima

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

D2 D1:

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

Este comportamiento no es sólo una casualidad de la aplicación; se garantiza in the documentation:

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

+3

Tus ejemplos fallarán (produciendo un TypeError) en Python 3.2, y en las versiones actuales de Jython, PyPy y IronPython: para esas versiones de Python, al pasar un dict con la notación '**', todas las claves de ese dict deberían ser cuerdas. Consulte el subproceso python-dev que comienza en http://mail.python.org/pipermail/python-dev/2010-April/099427.html para obtener más información. –

+0

@Mark: Gracias por el aviso. He editado el código para hacerlo compatible con implementaciones que no sean 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

11

Si quieres d1 que tienen prioridad en los conflictos, hacer:

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

De lo contrario, invierta d2 y d1.

1

Mi solución es definir una función de fusió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 hay claves duplicadas en esos diccionarios, la clave del diccionario más a la derecha en la lista de argumentos gana.

+1

Un bucle simple con una llamada '.update' en él (' merged = {} 'seguido de' para d en dict: fusionado.update (d) ') sería más corto, más legible y más eficiente. –

+1

O si realmente quiere usar 'reduce' y' lambda's, ¿qué tal 'return reduce (lambda x, y: x.update (y) o x, dicts, {})'? –

+0

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 puede tomar varios argumentos del diccionario con la misma funcionalidad.Es mejor no usar x.update (y) debajo de la lambda, porque siempre devuelve _None_. Y estoy tratando de escribir una función más general _merge \ _with_ que tome varios números de argumento del diccionario y trate con las claves duplicadas con la función suministrada. Una vez que haya terminado, lo publicaré en otro hilo donde la solución es más relevante. –

0

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

Aunque, quiero señalar que dict(d1, **d2) es en realidad una mala manera de combinar Diccionarios en general, ya que los argumentos de palabras clave deben ser cadenas, por tanto, se producirá un error si tiene un dict tales como:

{ 
    1: 'foo', 
    2: 'bar' 
} 
Cuestiones relacionadas