Estoy buscando una manera de actualizar dict dictionary1 con el contenido de la actualización dict sin sobrescribir el nivel A
dictionary1={'level1':{'level2':{'levelA':0,'levelB':1}}}
update={'level1':{'level2':{'levelB':10}}}
dictionary1.update(update)
print dictionary1
{'level1': {'level2': {'levelB': 10}}}
Sé que la actualización elimina los valores en el nivel 2 porque está actualizando la clave más baja nivel1.
¿Cómo podría abordar esto, dado que dictionary1 y update pueden tener cualquier extensión?
Respuestas:
La respuesta de @ FM tiene la idea general correcta, es decir, una solución recursiva, pero una codificación algo peculiar y al menos un error. Yo recomendaría, en cambio:
Python 2:
Python 3:
El error aparece cuando la "actualización" tiene un elemento
k
,v
dondev
es undict
yk
no es originalmente una clave en el diccionario que se está actualizando - el código de @ FM "omite" esta parte de la actualización (porque lo realiza en un nuevo vacíodict
que no se guarda ni se devuelve en ningún lugar, solo se pierde cuando vuelve la llamada recursiva).Mis otros cambios son menores: no hay ninguna razón para el
if
/else
construct cuando.get
hace el mismo trabajo más rápido y limpio, yisinstance
se aplica mejor a las clases base abstractas (no a las concretas) por generalidad.fuente
isinstance
prueba, pero pensé en intentarlo.TypeError: 'int' object does not support item assignment.
cuando usted, por ejemploupdate({'k1': 1}, {'k1': {'k2': 2}})
. Para cambiar este comportamiento y, en su lugar, expandir la profundidad de los diccionarios para dar cabida a diccionarios más profundos, puede agregar unelif isinstance(d, Mapping):
alrededor ded[k] = u[k]
laisinstance
condición y después de ella . También necesitará agregar unelse: d = {k: u[k]}
para lidiar con el caso de que el dict de actualización es más profundo que el dict original. Feliz de editar la respuesta, pero no quiero ensuciar el código conciso que resuelve el problema del OP.isinstance(v, collections.Mapping)
lugar deisinstance(v, dict)
? En el caso de que OP decida comenzar a usar colecciones?u.iteritems()
au.items()
, de lo contrario encontrará:AttributeError: 'dict' object has no attribute 'iteritems'
Me tomó un poco en este caso, pero gracias a la publicación de @ Alex, completó el vacío que me faltaba. Sin embargo, me encontré con un problema si un valor dentro del recursivo
dict
resulta ser unlist
, así que pensé en compartirlo y ampliar su respuesta.fuente
orig_dict.get(key, []) + val
.merged_tree = update({'default': {'initialvalue': 1}}, other_tree)
@ La respuesta de Alex es buena, pero no funciona cuando se reemplaza un elemento como un entero con un diccionario, como
update({'foo':0},{'foo':{'bar':1}})
. Esta actualización lo aborda:fuente
elif
verificación del tipo de objeto original fuera un condicional "envolvente" que contiene las comprobaciones tanto del valor como de la clave de ese dict / mapping. Inteligente.update({'A1': 1, 'A2':2}, {'A1': {'B1': {'C1': 3, 'C2':4}, 'B2':2}, 'A3':5})
. ¿Tienes un ejemplo que no hace lo que quieres?if isinstance(d, collections.Mapping)
en cada iteración? Mira mi respuesta .La misma solución que la aceptada, pero un nombre variable más claro, una cadena de documentos y un error corregido donde,
{}
como valor, no se anularía.Aquí hay algunos casos de prueba:
Esta función está disponible en el paquete charlatán , en
charlatan.utils
.fuente
Aquí hay una versión inmutable de la fusión recursiva de diccionarios en caso de que alguien la necesite.
Basado en la respuesta de @Alex Martelli .
Python 2.x:
Python 3.x:
fuente
Pequeñas mejoras en la respuesta de @ Alex que permite actualizar diccionarios de diferentes profundidades, así como limitar la profundidad en que la actualización se sumerge en el diccionario anidado original (pero la profundidad de actualización del diccionario no está limitada). Solo unos pocos casos han sido probados:
fuente
update({'k1': 1}, {'k1': {'k2': {'k3': 3}}})
agregué una respuesta que aborda estoif isinstance(d, Mapping)
en cada iteración? Mira mi respuesta . (Además, no estoy seguro de tud = {k: u[k]}
)Esta pregunta es antigua, pero aterricé aquí cuando buscaba una solución de "fusión profunda". Las respuestas anteriores inspiraron lo que sigue. Terminé escribiendo el mío porque había errores en todas las versiones que probé. El punto crítico perdido fue, a cierta profundidad arbitraria de los dos dictados de entrada, para alguna clave, k, el árbol de decisión cuando d [k] o u [k] no es un dict fue defectuoso.
Además, esta solución no requiere recursividad, que es más simétrica con el
dict.update()
funcionamiento y los retornosNone
.fuente
Simplemente use
python-benedict
(lo hice) , tiene unmerge
método de utilidad (actualización profunda) y muchos otros. Funciona con python 2 / python 3 y está bien probado.Instalación:
pip install python-benedict
Documentación: https://github.com/fabiocaccamo/python-benedict
fuente
En ninguna de estas respuestas, los autores parecen entender el concepto de actualizar un objeto almacenado en un diccionario, ni siquiera de iterar sobre elementos del diccionario (a diferencia de las claves). Así que tuve que escribir uno que no haga inútiles almacenes y recuperaciones de diccionarios tautológicos. Se supone que los dictos almacenan otros dictados o tipos simples.
O incluso más simple trabajando con cualquier tipo:
fuente
Actualice la respuesta de @Alex Martelli para corregir un error en su código para hacer que la solución sea más sólida:
La clave es que a menudo queremos crear el mismo tipo en la recursividad, por lo que aquí usamos
v.copy().clear()
pero no{}
. Y esto es especialmente útil si eldict
here es de tipocollections.defaultdict
que puede tener diferentes tipos dedefault_factory
s.Observe también que
u.iteritems()
se ha cambiado au.items()
inPython3
.fuente
Usé la solución que sugiere @Alex Martelli, pero falla
TypeError 'bool' object does not support item assignment
cuando los dos diccionarios difieren en tipo de datos en algún nivel.
En el caso de que al mismo nivel, el elemento del diccionario
d
sea solo un escalar (es decirBool
), mientras que el elemento del diccionariou
sigue siendo el diccionario, la reasignación falla ya que no es posible la asignación del diccionario al escalar (comoTrue[k]
).Una condición adicional corrige que:
fuente
El siguiente código debería resolver el
update({'k1': 1}, {'k1': {'k2': 2}})
problema en la respuesta de @Alex Martelli de la manera correcta.fuente
usar
dict
ocollections.Mapping
fuente
Sé que esta pregunta es bastante antigua, pero aún publico lo que hago cuando tengo que actualizar un diccionario anidado. Podemos usar el hecho de que los dictados se pasan por referencia en python Suponiendo que la ruta de la clave es conocida y está separada por puntos. Forex si tenemos un dict llamado datos:
Y queremos actualizar la clase de cola, la ruta de la clave sería:
log_config_worker.handlers.queue.class
Podemos usar la siguiente función para actualizar el valor:
Esto actualizaría el diccionario correctamente.
fuente
Podría ser que tropieces con un diccionario no estándar, como yo hoy, que no tiene atributos de iteritems. En este caso, es fácil interpretar este tipo de diccionario como un diccionario estándar. Por ejemplo: Python 2.7:
Python 3.8:
fuente
¡Si! Y otra solución. Mi solución difiere en las teclas que se están comprobando. En todas las demás soluciones solo miramos las claves en
dict_b
. Pero aquí miramos en la unión de ambos diccionarios.Hazlo como quieras
fuente
Si desea reemplazar un "diccionario anidado completo con matrices", puede usar este fragmento:
Reemplazará cualquier "viejo_valor" por "nuevo_valor". Se trata más o menos de una reconstrucción profunda del diccionario. Incluso puede funcionar con List o Str / int dado como parámetro de entrada de primer nivel.
fuente
Otra forma de usar la recursividad:
fuente
un nuevo Q cómo hacerlo por una cadena de llaves
fuente
podrías probar esto, funciona con listas y es puro:
fuente
Recomiendo reemplazar
{}
portype(v)()
para propagar el tipo de objeto de cualquier subclase dict almacenadau
pero ausented
. Por ejemplo, esto preservaría tipos como colecciones. OrderedDict:Python 2:
Python 3:
fuente
Eso es un poco al margen, pero ¿realmente necesita diccionarios anidados? Dependiendo del problema, a veces un diccionario plano puede ser suficiente ... y lucir bien:
fuente
Si quieres una sola línea:
fuente