Vue d'ensemble
L'approche suivante subdivise le problème d'une fusion profonde de fichiers en plusieurs parties :
-
Une fonction de fusion superficielle paramétrée merge(f)(a,b)
qui utilise un fonction f
pour fusionner deux dicts a
et b
-
Une fonction de fusion récursive f
à utiliser conjointement avec merge
Mise en œuvre
Une fonction permettant de fusionner deux dicts (non imbriqués) peut être écrite de différentes manières. Personnellement, j'aime bien
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
Une bonne façon de définir une fonction de fusion récursive appropriée f
utilise multipledispatch qui permet de définir des fonctions qui s'évaluent selon différents chemins en fonction du type de leurs arguments.
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)
Exemple
Pour fusionner deux dicts imbriqués, il suffit d'utiliser merge(f)
par exemple :
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'}}
Notes :
Les avantages de cette approche sont les suivants :
-
La fonction est construite à partir de fonctions plus petites qui font chacune une seule chose ce qui rend le code plus simple à raisonner et à tester.
-
Le comportement n'est pas codé en dur, mais peut être modifié et étendu selon les besoins, ce qui améliore la réutilisation du code (voir l'exemple ci-dessous).
Personnalisation
Certaines réponses ont également pris en compte les dicts qui contiennent des listes, par exemple, d'autres dicts (potentiellement imbriqués). Dans ce cas, il peut être utile de passer en revue les listes et de les fusionner en fonction de leur position. Cela peut être fait en ajoutant une autre définition à la fonction de fusion f
:
import itertools
@dispatch(list, list)
def f(a,b):
return [merge(f)(*arg) for arg in itertools.zip_longest(a, b)]