two dictionaries python dictionary merge array-merge

python - dictionaries - Diccionarios de diccionarios se fusionan



merge two dictionaries python (19)

Diccionarios de diccionarios se fusionan

Como esta es la pregunta canónica (a pesar de ciertas no generalidades) estoy proporcionando el enfoque canónico de Pythonic para resolver este problema.

Caso más simple: "las hojas son dictados anidados que terminan en dicts vacíos":

d1 = {''a'': {1: {''foo'': {}}, 2: {}}} d2 = {''a'': {1: {}, 2: {''bar'': {}}}} d3 = {''b'': {3: {''baz'': {}}}} d4 = {''a'': {1: {''quux'': {}}}}

Este es el caso más simple para la recursión, y recomendaría dos enfoques ingenuos:

def rec_merge1(d1, d2): ''''''return new merged dict of dicts'''''' for k, v in d1.items(): # in Python 2, use .iteritems()! if k in d2: d2[k] = rec_merge1(v, d2[k]) d3 = d1.copy() d3.update(d2) return d3 def rec_merge2(d1, d2): ''''''update first dict with second recursively'''''' for k, v in d1.items(): # in Python 2, use .iteritems()! if k in d2: d2[k] = rec_merge2(v, d2[k]) d1.update(d2) return d1

Creo que preferiría el segundo al primero, pero tenga en cuenta que el estado original del primero debería reconstruirse desde su origen. Aquí está el uso:

>>> from functools import reduce # only required for Python 3. >>> reduce(rec_merge1, (d1, d2, d3, d4)) {''a'': {1: {''quux'': {}, ''foo'': {}}, 2: {''bar'': {}}}, ''b'': {3: {''baz'': {}}}} >>> reduce(rec_merge2, (d1, d2, d3, d4)) {''a'': {1: {''quux'': {}, ''foo'': {}}, 2: {''bar'': {}}}, ''b'': {3: {''baz'': {}}}}

Caso complejo: "hojas son de cualquier otro tipo:"

Entonces, si terminan en dicts, es un caso simple de fusión de los dicts vacíos finales. Si no, no es tan trivial. Si las cadenas, ¿cómo las fusionas? Los conjuntos se pueden actualizar de manera similar, por lo que podríamos dar ese tratamiento, pero perdemos el orden en el que se fusionaron. Entonces, ¿importa el orden?

Entonces, en lugar de más información, el enfoque más simple será darles el tratamiento de actualización estándar si ambos valores no son dictados: es decir, el valor del segundo dicto sobrescribirá el primero, incluso si el valor del segundo dic es Ninguno y el primero es un valor dict con mucha información.

d1 = {''a'': {1: ''foo'', 2: None}} d2 = {''a'': {1: None, 2: ''bar''}} d3 = {''b'': {3: ''baz''}} d4 = {''a'': {1: ''quux''}} from collections import MutableMapping def rec_merge(d1, d2): '''''' Update two dicts of dicts recursively, if either mapping has leaves that are non-dicts, the second''s leaf overwrites the first''s. '''''' for k, v in d1.items(): # in Python 2, use .iteritems()! if k in d2: # this next check is the only difference! if all(isinstance(e, MutableMapping) for e in (v, d2[k])): d2[k] = rec_merge(v, d2[k]) # we could further check types and merge as appropriate here. d3 = d1.copy() d3.update(d2) return d3

Y ahora

from functools import reduce reduce(rec_merge, (d1, d2, d3, d4))

devoluciones

{''a'': {1: ''quux'', 2: ''bar''}, ''b'': {3: ''baz''}}

Aplicación a la pregunta original:

He tenido que quitar las llaves alrededor de las letras y ponerlas en comillas simples para que esto sea legítimo Python (de lo contrario se establecerían literales en Python 2.7+) y anexar una llave faltante:

dict1 = {1:{"a":''A''}, 2:{"b":''B''}} dict2 = {2:{"c":''C''}, 3:{"d":''D''}}

y rec_merge(dict1, dict2) ahora devuelve:

{1: {''a'': ''A''}, 2: {''c'': ''C'', ''b'': ''B''}, 3: {''d'': ''D''}}

Que coincide con el resultado deseado de la pregunta original (después de cambiar, por ejemplo, {A} a ''A'' ).

Necesito fusionar varios diccionarios, esto es lo que tengo, por ejemplo:

dict1 = {1:{"a":{A}},2:{"b":{B}}} dict2 = {2:{"c":{C}}, 3:{"d":{D}}

Con A B C y D siendo hojas del árbol, como {"info1":"value", "info2":"value2"}

Hay un nivel desconocido (profundidad) de diccionarios, podría ser {2:{"c":{"z":{"y":{C}}}}}

En mi caso, representa una estructura de directorio / archivos con nodos que son documentos y deja de ser archivos.

Quiero fusionarlos para obtener dict3={1:{"a":{A}},2:{"b":{B},"c":{C}},3:{"d":{D}}}

No estoy seguro de cómo podría hacerlo fácilmente con Python.


Aquí hay una manera fácil de hacerlo usando generadores:

def mergedicts(dict1, dict2): for k in set(dict1.keys()).union(dict2.keys()): if k in dict1 and k in dict2: if isinstance(dict1[k], dict) and isinstance(dict2[k], dict): yield (k, dict(mergedicts(dict1[k], dict2[k]))) else: # If one of the values is not a dict, you can''t continue merging it. # Value from second dict overrides one in first and we move on. yield (k, dict2[k]) # Alternatively, replace this with exception raiser to alert you of value conflicts elif k in dict1: yield (k, dict1[k]) else: yield (k, dict2[k]) dict1 = {1:{"a":"A"},2:{"b":"B"}} dict2 = {2:{"c":"C"},3:{"d":"D"}} print dict(mergedicts(dict1,dict2))

Esto imprime:

{1: {''a'': ''A''}, 2: {''c'': ''C'', ''b'': ''B''}, 3: {''d'': ''D''}}


Basado en @andrew cooke. Esta versión maneja listas anidadas de dicts y también permite la opción de actualizar los valores

def merge(a, b, path=None, update=True): "http://.com/questions/7204805/python-dictionaries-of-dictionaries-merge" "merges b into a" if path is None: path = [] for key in b: if key in a: if isinstance(a[key], dict) and isinstance(b[key], dict): merge(a[key], b[key], path + [str(key)]) elif a[key] == b[key]: pass # same leaf value elif isinstance(a[key], list) and isinstance(b[key], list): for idx, val in enumerate(b[key]): a[key][idx] = merge(a[key][idx], b[key][idx], path + [str(key), str(idx)], update=update) elif update: a[key] = b[key] else: raise Exception(''Conflict at %s'' % ''.''.join(path + [str(key)])) else: a[key] = b[key] return a


Basado en las respuestas de @andrew cooke. Se ocupa de listas anidadas de una mejor manera.

def deep_merge_lists(original, incoming): """ Deep merge two lists. Modifies original. Reursively call deep merge on each correlated element of list. If item type in both elements are a. dict: call deep_merge_dicts on both values. b. list: Calls deep_merge_lists on both values. c. any other type: Value is overridden. d. conflicting types: Value is overridden. If length of incoming list is more that of original then extra values are appended. """ common_length = min(len(original), len(incoming)) for idx in range(common_length): if isinstance(original[idx], dict) and isinstance(incoming[idx], dict): deep_merge_dicts(original[idx], incoming[idx]) elif isinstance(original[idx], list) and isinstance(incoming[idx], list): deep_merge_lists(original[idx], incoming[idx]) else: orginal[idx] = incoming[idx] for idx in range(common_length, len(incoming)): original.append(incoming[idx]) def deep_merge_dicts(original, incoming): """ Deep merge two dictionaries. Modfies original. For key conflicts if both values are: a. dict: Recursivley call deep_merge_dicts on both values. b. list: Calls deep_merge_lists on both values. c. any other type: Value is overridden. d. conflicting types: Value is overridden. """ for key in incoming: if key in original: if isinstance(original[key], dict) and isinstance(incoming[key], dict): deep_merge_dicts(original[key], incoming[key]) elif isinstance(original[key], list) and isinstance(incoming[key], list): deep_merge_lists(original[key], incoming[key]) else: original[key] = incoming[key] else: original[key] = incoming[key]


Dado que dictviews son compatibles con las operaciones de configuración, pude simplificar enormemente la respuesta de jterrace.

def merge(dict1, dict2): for k in dict1.keys() - dict2.keys(): yield (k, dict1[k]) for k in dict2.keys() - dict1.keys(): yield (k, dict2[k]) for k in dict1.keys() & dict2.keys(): yield (k, dict(merge(dict1[k], dict2[k])))

Cualquier intento de combinar un dict con un no dicto (técnicamente, un objeto con un método de ''claves'' y un objeto sin un método de ''claves'') levantará un AttributeError. Esto incluye tanto la llamada inicial a la función como las llamadas recursivas. Esto es exactamente lo que quería, así que lo dejé. Podrías atrapar fácilmente un AttributeErrors arrojado por la llamada recursiva y luego arrojar el valor que desees.


El código dependerá de tus reglas para resolver conflictos de combinación, por supuesto. Aquí hay una versión que puede tomar un número arbitrario de argumentos y los combina recursivamente a una profundidad arbitraria, sin usar ninguna mutación de objeto. Utiliza las siguientes reglas para resolver conflictos de combinación:

  • los diccionarios tienen prioridad sobre los valores que no son dict ( {"foo": {...}} tiene prioridad sobre {"foo": "bar"} )
  • los argumentos posteriores tienen prioridad sobre los anteriores (si combina {"a": 1} , {"a", 2} y {"a": 3} en orden, el resultado será {"a": 3} )

try: from collections import Mapping except ImportError: Mapping = dict def merge_dicts(*dicts): """ Return a new dictionary that is the result of merging the arguments together. In case of conflicts, later arguments take precedence over earlier arguments. """ updated = {} # grab all keys keys = set() for d in dicts: keys = keys.union(set(d)) for key in keys: values = [d[key] for d in dicts if key in d] # which ones are mapping types? (aka dict) maps = [value for value in values if isinstance(value, Mapping)] if maps: # if we have any mapping types, call recursively to merge them updated[key] = merge_dicts(*maps) else: # otherwise, just grab the last value we have, since later arguments # take precedence over earlier arguments updated[key] = values[-1] return updated


Esta versión de la función representará N número de diccionarios, y solo diccionarios; no se pueden pasar parámetros incorrectos, o generará un TypeError. La fusión en sí misma explica los conflictos clave, y en lugar de sobreescribir datos de un diccionario más abajo en la cadena de fusión, crea un conjunto de valores y se agrega a eso; no se pierde ningún dato

Puede que no sea el más eficiente en la página, pero es el más completo y no perderás ninguna información cuando combines tus 2 a N dicts.

def merge_dicts(*dicts): if not reduce(lambda x, y: isinstance(y, dict) and x, dicts, True): raise TypeError, "Object in *dicts not of type dict" if len(dicts) < 2: raise ValueError, "Requires 2 or more dict objects" def merge(a, b): for d in set(a.keys()).union(b.keys()): if d in a and d in b: if type(a[d]) == type(b[d]): if not isinstance(a[d], dict): ret = list({a[d], b[d]}) if len(ret) == 1: ret = ret[0] yield (d, sorted(ret)) else: yield (d, dict(merge(a[d], b[d]))) else: raise TypeError, "Conflicting key:value type assignment" elif d in a: yield (d, a[d]) elif d in b: yield (d, b[d]) else: raise KeyError return reduce(lambda x, y: dict(merge(x, y)), dicts[1:], dicts[0]) print merge_dicts({1:1,2:{1:2}},{1:2,2:{3:1}},{4:4})

salida: {1: [1, 2], 2: {1: 2, 3: 1}, 4: 4}


Este sencillo procedimiento recursivo fusionará un diccionario en otro mientras anula las claves en conflicto:

#!/usr/bin/env python2.7 def merge_dicts(dict1, dict2): """ Recursively merges dict2 into dict1 """ if not isinstance(dict1, dict) or not isinstance(dict2, dict): return dict2 for k in dict2: if k in dict1: dict1[k] = merge_dicts(dict1[k], dict2[k]) else: dict1[k] = dict2[k] return dict1 print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {2:{"c":"C"}, 3:{"d":"D"}})) print (merge_dicts({1:{"a":"A"}, 2:{"b":"B"}}, {1:{"a":"A"}, 2:{"b":"C"}}))

Salida:

{1: {''a'': ''A''}, 2: {''c'': ''C'', ''b'': ''B''}, 3: {''d'': ''D''}} {1: {''a'': ''A''}, 2: {''b'': ''C''}}


Esto debería ayudar a fusionar todos los elementos de dict2 en dict1 :

for item in dict2: if item in dict1: for leaf in dict2[item]: dict1[item][leaf] = dict2[item][leaf] else: dict1[item] = dict2[item]

Por favor, pruébelo y díganos si esto es lo que quería.

EDITAR:

La solución mencionada combina un solo nivel, pero resuelve correctamente el ejemplo dado por OP. Para combinar varios niveles, se debe usar la recursión.


Estuve probando tus soluciones y decidí utilizar esta en mi proyecto:

def mergedicts(dict1, dict2, conflict, no_conflict): for k in set(dict1.keys()).union(dict2.keys()): if k in dict1 and k in dict2: yield (k, conflict(dict1[k], dict2[k])) elif k in dict1: yield (k, no_conflict(dict1[k])) else: yield (k, no_conflict(dict2[k])) dict1 = {1:{"a":"A"}, 2:{"b":"B"}} dict2 = {2:{"c":"C"}, 3:{"d":"D"}} #this helper function allows for recursion and the use of reduce def f2(x, y): return dict(mergedicts(x, y, f2, lambda x: x)) print dict(mergedicts(dict1, dict2, f2, lambda x: x)) print dict(reduce(f2, [dict1, dict2]))

Pasar funciones como parámetros es clave para extender la solución jterrace para que se comporte como todas las demás soluciones recursivas.


Hay un pequeño problema con la respuesta de andrew cookes: en algunos casos, modifica el segundo argumento b cuando modificas el dict devuelto. Específicamente es debido a esta línea:

if key in a: ... else: a[key] = b[key]

Si b[key] es un dict , simplemente se asignará a a , lo que significa que cualquier modificación posterior a ese dict afectará tanto a como b .

a={} b={''1'':{''2'':''b''}} c={''1'':{''3'':''c''}} merge(merge(a,b), c) # {''1'': {''3'': ''c'', ''2'': ''b''}} a # {''1'': {''3'': ''c'', ''2'': ''b''}} (as expected) b # {''1'': {''3'': ''c'', ''2'': ''b''}} <---- c # {''1'': {''3'': ''c''}} (unmodified)

Para solucionar esto, la línea tendría que ser sustituida por esto:

if isinstance(b[key], dict): a[key] = clone_dict(b[key]) else: a[key] = b[key]

Donde clone_dict es:

def clone_dict(obj): clone = {} for key, value in obj.iteritems(): if isinstance(value, dict): clone[key] = clone_dict(value) else: clone[key] = value return

Todavía. Obviamente, esto no tiene en cuenta la list , el set y otras cosas, pero espero que ilustre las dificultades al intentar fusionar los dicts .

Y, para completar, esta es mi versión, donde puedes pasarle múltiples dicts :

def merge_dicts(*args): def clone_dict(obj): clone = {} for key, value in obj.iteritems(): if isinstance(value, dict): clone[key] = clone_dict(value) else: clone[key] = value return def merge(a, b, path=[]): for key in b: if key in a: if isinstance(a[key], dict) and isinstance(b[key], dict): merge(a[key], b[key], path + [str(key)]) elif a[key] == b[key]: pass else: raise Exception(''Conflict at `{path}/'''.format(path=''.''.join(path + [str(key)]))) else: if isinstance(b[key], dict): a[key] = clone_dict(b[key]) else: a[key] = b[key] return a return reduce(merge, args, {})


La manera más fácil en que puedo pensar es:

#!/usr/bin/python from copy import deepcopy def dict_merge(a, b): if not isinstance(b, dict): return b result = deepcopy(a) for k, v in b.iteritems(): if k in result and isinstance(result[k], dict): result[k] = dict_merge(result[k], v) else: result[k] = deepcopy(v) return result a = {1:{"a":''A''}, 2:{"b":''B''}} b = {2:{"c":''C''}, 3:{"d":''D''}} print dict_merge(a,b)

Salida:

{1: {''a'': ''A''}, 2: {''c'': ''C'', ''b'': ''B''}, 3: {''d'': ''D''}}


Si tiene un nivel desconocido de diccionarios, entonces sugeriría una función recursiva:

def combineDicts(dictionary1, dictionary2): output = {} for item, value in dictionary1.iteritems(): if dictionary2.has_key(item): if isinstance(dictionary2[item], dict): output[item] = combineDicts(value, dictionary2.pop(item)) else: output[item] = value for item, value in dictionary2.iteritems(): output[item] = value return output


Tenía dos diccionarios ( b ) que podían contener cualquier cantidad de diccionarios anidados. Quería fusionarlos de forma recursiva, con b tomando precedencia sobre a .

Considerando los diccionarios anidados como árboles, lo que quería era:

  • Para actualizar a para que cada ruta a cada hoja en b se represente en a
  • Para sobrescribir subárboles de a si se encuentra una hoja en la ruta correspondiente en b
    • Mantenga la invariante de que todos los nodos de la hoja b permanecen hojas.

Las respuestas existentes fueron un poco complicadas para mi gusto y dejaron algunos detalles en el estante. He pirateado juntos lo siguiente, que pasa pruebas unitarias para mi conjunto de datos.

def merge_map(a, b): if not isinstance(a, dict) or not isinstance(b, dict): return b for key in b.keys(): a[key] = merge_map(a[key], b[key]) if key in a else b[key] return a

Ejemplo (formateado para mayor claridad):

a = { 1 : {''a'': ''red'', ''b'': {''blue'': ''fish'', ''yellow'': ''bear'' }, ''c'': { ''orange'': ''dog''}, }, 2 : {''d'': ''green''}, 3: ''e'' } b = { 1 : {''b'': ''white''}, 2 : {''d'': ''black''}, 3: ''e'' } >>> merge_map(a, b) {1: {''a'': ''red'', ''b'': ''white'', ''c'': {''orange'': ''dog''},}, 2: {''d'': ''black''}, 3: ''e''}

Los caminos en b que necesitaban mantenerse fueron:

  • 1 -> ''b'' -> ''white''
  • 2 -> ''d'' -> ''black''
  • 3 -> ''e'' .

a tenía las rutas únicas y no conflictivas de:

  • 1 -> ''a'' -> ''red''
  • 1 -> ''c'' -> ''orange'' -> ''dog''

por lo que todavía están representados en el mapa combinado.


Tengo otra solución ligeramente diferente aquí:

def deepMerge(d1, d2, inconflict = lambda v1,v2 : v2) : '''''' merge d2 into d1. using inconflict function to resolve the leaf conflicts '''''' for k in d2: if k in d1 : if isinstance(d1[k], dict) and isinstance(d2[k], dict) : deepMerge(d1[k], d2[k], inconflict) elif d1[k] != d2[k] : d1[k] = inconflict(d1[k], d2[k]) else : d1[k] = d2[k] return d1

Por defecto, resuelve conflictos a favor de los valores del segundo dict, pero puedes anularlo fácilmente, con algo de brujería puedes incluso lanzar excepciones. :).


Un problema con esta pregunta es que los valores de la dict pueden ser datos arbitrariamente complejos. En base a estas y otras respuestas, se me ocurrió este código:

class YamlReaderError(Exception): pass def data_merge(a, b): """merges b into a and return merged result NOTE: tuples and arbitrary objects are not handled as it is totally ambiguous what should happen""" key = None # ## debug output # sys.stderr.write("DEBUG: %s to %s/n" %(b,a)) try: if a is None or isinstance(a, str) or isinstance(a, unicode) or isinstance(a, int) or isinstance(a, long) or isinstance(a, float): # border case for first run or if a is a primitive a = b elif isinstance(a, list): # lists can be only appended if isinstance(b, list): # merge lists a.extend(b) else: # append to list a.append(b) elif isinstance(a, dict): # dicts must be merged if isinstance(b, dict): for key in b: if key in a: a[key] = data_merge(a[key], b[key]) else: a[key] = b[key] else: raise YamlReaderError(''Cannot merge non-dict "%s" into dict "%s"'' % (b, a)) else: raise YamlReaderError(''NOT IMPLEMENTED "%s" into "%s"'' % (b, a)) except TypeError, e: raise YamlReaderError(''TypeError "%s" in key "%s" when merging "%s" into "%s"'' % (e, key, b, a)) return a

Mi caso de uso es fusionar archivos YAML donde solo tengo que tratar con un subconjunto de posibles tipos de datos. Por lo tanto, puedo ignorar las tuplas y otros objetos. Para mí, una lógica de fusión sensata significa

  • reemplazar escalares
  • añadir listas
  • fusionar dictados mediante la adición de claves que faltan y la actualización de las claves existentes

Todo lo demás y lo imprevisto resulta en un error.


esto en realidad es bastante complicado, especialmente si desea un mensaje de error útil cuando las cosas son inconsistentes, mientras acepta correctamente entradas duplicadas pero consistentes (algo que ninguna otra respuesta aquí sí ...)

suponiendo que no tiene un gran número de entradas, una función recursiva es más fácil:

def merge(a, b, path=None): "merges b into a" if path is None: path = [] for key in b: if key in a: if isinstance(a[key], dict) and isinstance(b[key], dict): merge(a[key], b[key], path + [str(key)]) elif a[key] == b[key]: pass # same leaf value else: raise Exception(''Conflict at %s'' % ''.''.join(path + [str(key)])) else: a[key] = b[key] return a # works print(merge({1:{"a":"A"},2:{"b":"B"}}, {2:{"c":"C"},3:{"d":"D"}})) # has conflict merge({1:{"a":"A"},2:{"b":"B"}}, {1:{"a":"A"},2:{"b":"C"}})

tenga en cuenta que esto muta a - los contenidos de b se agregan a a (que también se devuelve). si quieres mantener a , podrías llamarla como merge(dict(a), b) .

agf señaló (a continuación) que puede tener más de dos dictados, en cuyo caso puede usar:

reduce(merge, [dict1, dict2, dict3...])

donde todo se agregará a dict1.

[nota - edité mi respuesta inicial para mutar el primer argumento; eso hace que el "reducir" sea más fácil de explicar]

ps en python 3, también necesitarás from functools import reduce


Visión de conjunto

El siguiente enfoque subdivide el problema de una fusión profunda de dictados en:

  1. Una fusión de función de merge(f)(a,b) superficial parametrizada merge(f)(a,b) que usa una función f para unir dos dictados a y b

  2. Una función de fusión recursiva f para ser utilizada junto con la merge

Implementación

Una función para fusionar dos dictados (no anidados) se puede escribir de muchas maneras. Personalmente me gusta

def merge(f): def merge(a,b): keys = a.keys() | b.keys() return {key:f(*[a.get(key), b.get(key)]) for key in keys} return merge

Una buena forma de definir una función de fusión recurrente apropiada f es multipledispatch que permite definir funciones que se evalúan a lo largo de diferentes rutas según el tipo de sus argumentos.

from multipledispatch import dispatch #for anything that is not a dict return @dispatch(object, object) def f(a, b): return b if b is not None else a #for dicts recurse @dispatch(dict, dict) def f(a,b): return merge(f)(a,b)

Ejemplo

Para unir dos dictados anidados, simplemente use merge(f) por ejemplo:

dict1 = {1:{"a":"A"},2:{"b":"B"}} dict2 = {2:{"c":"C"},3:{"d":"D"}} merge(f)(dict1, dict2) #returns {1: {''a'': ''A''}, 2: {''b'': ''B'', ''c'': ''C''}, 3: {''d'': ''D''}}

Notas:

Las ventajas de este enfoque son:

  • La función es construir a partir de funciones más pequeñas que hacen una sola cosa que hace que el código sea más simple de razonar y probar

  • El comportamiento no está codificado, pero puede modificarse y ampliarse según sea necesario, lo que mejora la reutilización del código (ver ejemplo a continuación).

Personalización

Algunas respuestas también consideraron los dictados que contienen listas, por ejemplo, de otros dicts (potencialmente anidados). En este caso, uno podría querer mapear las listas y fusionarlas según la posición. Esto se puede hacer agregando otra definición a la función de fusión f :

import itertools @dispatch(list, list) def f(a,b): return [merge(f)(*arg) for arg in itertools.zip_longest(a,b,fillvalue={})]


class Utils(object): """ >>> a = { ''first'' : { ''all_rows'' : { ''pass'' : ''dog'', ''number'' : ''1'' } } } >>> b = { ''first'' : { ''all_rows'' : { ''fail'' : ''cat'', ''number'' : ''5'' } } } >>> Utils.merge_dict(b, a) == { ''first'' : { ''all_rows'' : { ''pass'' : ''dog'', ''fail'' : ''cat'', ''number'' : ''5'' } } } True >>> main = {''a'': {''b'': {''test'': ''bug''}, ''c'': ''C''}} >>> suply = {''a'': {''b'': 2, ''d'': ''D'', ''c'': {''test'': ''bug2''}}} >>> Utils.merge_dict(main, suply) == {''a'': {''b'': {''test'': ''bug''}, ''c'': ''C'', ''d'': ''D''}} True """ @staticmethod def merge_dict(main, suply): """ 获取融合的字典,以main为主,suply补充,冲突时以main为准 :return: """ for key, value in suply.items(): if key in main: if isinstance(main[key], dict): if isinstance(value, dict): Utils.merge_dict(main[key], value) else: pass else: pass else: main[key] = value return main if __name__ == ''__main__'': import doctest doctest.testmod()