92 votes

Comment fusionner des dictionnaires en Python ?

d3 = dict(d1, **d2)

Je comprends que cela fusionne le dictionnaire. Mais, est-il unique ? Que se passe-t-il si d1 a la même clé que d2 mais une valeur différente ? Je voudrais que d1 et d2 soient fusionnés, mais que d1 ait la priorité s'il y a un doublon de clé.

9 votes

Veuillez noter que cette astuce est considérée comme un abus de pouvoir. ** le passage de l'argument mot-clé à moins que toutes les clés de d2 sont des cordes. Si toutes les clés de d2 sont des chaînes de caractères, cela échoue dans Python 3.2, et dans les implémentations alternatives de Python comme Jython, IronPython et PyPy. Voir, par exemple, mail.python.org/pipermail/python-dev/2010-avril/099459.html .

2 votes

157voto

Felix Kling Points 247451

Vous pouvez utiliser le .update() si vous n'avez pas besoin de la méthode originale d2 plus :

Mettre à jour le dictionnaire avec les paires clé/valeur de l'autre, écraser des clés existantes . Retourner à None .

Par exemple :

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

Mise à jour :

Bien sûr, vous pouvez d'abord copier le dictionnaire afin de créer un nouveau dictionnaire fusionné. Cela peut s'avérer nécessaire ou non. Dans le cas où vous avez des objets composés (objets qui contiennent d'autres objets, comme des listes ou des instances de classe) dans votre dictionnaire, copy.deepcopy doit également être prise en compte.

1 votes

Dans ce cas, les éléments d1 devraient avoir la priorité si des clés conflictuelles sont trouvées.

0 votes

Au cas où vous en auriez encore besoin, faites-en une copie. d3 = d2.copy() d3.update(d1) mais j'aimerais que d1 + d2 soient ajoutés au langage.

4 votes

D1 + d2 est problématique car l'un des dictionnaires doit avoir la priorité pendant les conflits, et il n'est pas particulièrement évident de savoir lequel.

43voto

unutbu Points 222216

Dans Python2,

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

d1 prévaut sur d2 :

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

d2 a priorité sur d1 :

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

Ce comportement n'est pas un simple hasard de l'implémentation ; il est garanti. dans la documentation :

Si une clé est spécifiée à la fois dans l'argument et comme argument de mot-clé la valeur associée au le mot-clé est conservé dans le dictionnaire.

3 votes

Vos exemples échoueront (en produisant une TypeError) dans Python 3.2, et dans les versions actuelles de Jython, PyPy et IronPython : pour ces versions de Python, lorsque l'on passe un dict avec l'option ** toutes les clés de ce dict doivent être des chaînes de caractères. Voir le fil de discussion de python-dev à partir de mail.python.org/pipermail/python-dev/2010-avril/099427.html pour plus.

0 votes

@Mark : Merci pour l'info. J'ai modifié le code pour le rendre compatible avec les implémentations non-CPython.

3 votes

Il échoue si vos clés sont des tuples de chaînes et de nombres. par exemple, d1={(1,'a'):1, (1,'b'):0,} d2={(1,'a'):1, (2,'b'):2, (2,'a'):1,}

14voto

tzot Points 32224

Si vous voulez d1 pour avoir la priorité dans les conflits, faites :

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

Sinon, inverser d2 y d1 .

2voto

Xavier Guihot Points 6414

À partir de Python 3.9 l'opérateur | crée un nouveau dictionnaire avec les clés et les valeurs fusionnées de deux dictionnaires :

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

Ceci :

Crée un nouveau dictionnaire d3 avec les clés et valeurs fusionnées de d2 et d1. Les valeurs de d1 sont prioritaires lorsque d2 et d1 partagent des clés.


Notez également le |= opérateur qui modifie d2 en fusionnant d1, avec priorité sur les valeurs de d1 :

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

1voto

Lei Zhao Points 86

Ma solution consiste à définir un fusionner fonction. Elle n'est pas sophistiquée et ne coûte qu'une ligne. Voici le code 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()) }

Tests

>>> 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()
{}

Il fonctionne pour un nombre arbitraire d'arguments de dictionnaire. S'il y a des clés en double dans ces dictionnaires, la clé du dictionnaire le plus à droite dans la liste des arguments l'emporte.

1 votes

Une simple boucle avec un .update en elle ( merged={} suivi par for d in dict: merged.update(d) ) serait plus court, plus lisible et plus efficace.

1 votes

Ou si vous voulez vraiment utiliser reduce y lambda que diriez-vous de return reduce(lambda x, y: x.update(y) or x, dicts, {}) ?

1 votes

Vous pouvez essayer votre code dans le shell et voir s'il est correct. Ce que j'essaie de faire, c'est d'écrire une fonction qui peut prendre un nombre variable d'arguments de dictionnaire avec la même fonctionnalité. Il est préférable de ne pas utiliser x.update(y) sous le lambda, parce qu'il retourne toujours Aucun . Et j'essaie d'écrire une fonction plus générale fusionner_avec qui prennent un nombre variable d'arguments de dictionnaire et traitent les clés dupliquées avec la fonction fournie. Une fois que j'aurai terminé, je le posterai dans un autre fil où la solution est plus pertinente.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X