«Étendre» Python pour un dictionnaire


464

Quelle est la meilleure façon d'étendre un dictionnaire avec un autre? Par exemple:

>>> a = { "a" : 1, "b" : 2 }
>>> b = { "c" : 3, "d" : 4 }
>>> a
{'a': 1, 'b': 2}
>>> b
{'c': 3, 'd': 4}

Je recherche une opération pour obtenir cette forboucle d' évitement :

{ "a" : 1, "b" : 2, "c" : 3, "d" : 4 }

Je souhaite faire quelque chose comme:

a.extend(b)  # This does not work

25
Je savais pour les listes [], alors je suppose que ça peut être du travail pour les autres, pas extrange ;-)
FerranB

Réponses:


696

6
Après avoir lu la documentation, on comprendra pourquoi il y a une «mise à jour» mais pas une «extension».
georg

58
gardez à l'esprit que update () modifie directement le dict et renvoie None.
e18r

5
Mais que faire si vous souhaitez uniquement étendre le dict avec des valeurs qui ne sont pas déjà définies (c'est-à-dire sans écraser les valeurs existantes)? Par exemple, mettre à jour un dict {"a":2, "b":3}avec dict {"b":4, "c":5}to dict {"a":2, "b":3,"c":5}? Bien sûr, il est possible de l'utiliser update()en déplaçant des trucs, mais ce serait mieux si cela pouvait être accompli en une seule ligne ...
Nearoo

17
@Nearoo - mettez simplement à jour le chemin inverse; au lieu de x.update (y) [qui écraserait les valeurs x avec y], utilisez y.update (x) [qui écrase les valeurs y avec x] et utilisez y comme dict de votre choix pour d'autres opérations
jonathanl

gardez à l'esprit que l' updateécrasement des entrées existantes est silencieux. C'est-à-dire que toutes les valeurs bécrasent celles ades clés qui se chevauchent.
CGFoX

186

Un beau bijou dans cette question fermée :

La "voie oneliner", ne modifiant aucun des dits d'entrée, est

basket = dict(basket_one, **basket_two)

Découvrez ce que **basket_two(le **) signifie ici .

En cas de conflit, les éléments de basket_tworemplaceront ceux de basket_one. En ce qui concerne les lignes simples, c'est assez lisible et transparent, et je n'ai aucun inconvénient à l'utiliser à chaque fois qu'un dicton qui est un mélange de deux autres est utile (tout lecteur qui a du mal à le comprendre sera en fait très bien servi par la façon dont cela l'incite à se renseigner sur dictle **formulaire ;-). Ainsi, par exemple, utilise comme:

x = mungesomedict(dict(adict, **anotherdict))

sont des occurrences assez fréquentes dans mon code.

Présenté à l'origine par Alex Martelli

Remarque: En Python 3, cela ne fonctionnera que si chaque clé de basket_two est un string.


3
La documentation de dictest facile à trouver tandis qu'elle **est un peu plus délicate (le mot clé est kwargs ). Voici une belle explication: saltycrane.com/blog/2008/01/…
johndodo

4
cela peut être utilisé pour générer une seconde variable avec une seule commande, alors que basket_one.update(<dict>)comme son nom l'indique, met à jour un dictionnaire existant (ou cloné).
furins

2
Notez qu'en Python 3, les noms d'arguments, et donc les clés de **anotherdict, doivent être des chaînes.
Petr Viktorin

Merci @johndodo - J'ai intégré vos suggestions dans le post.
Tom Leys

1
Une belle alternative fonctionnelle à la réponse acceptée. Cette approche est souhaitable si vous devez utiliser directement le dict nouvellement combiné. (voir également le commentaire de @ e18r sur la réponse acceptée).
chinnychinchin le

45

Avez-vous essayé d'utiliser la compréhension du dictionnaire avec le mappage du dictionnaire:

a = {'a': 1, 'b': 2}
b = {'c': 3, 'd': 4}

c = {**a, **b}
# c = {"a": 1, "b": 2, "c": 3, "d": 4}

Une autre méthode consiste à utiliser dict (itérable, ** kwarg)

c = dict(a, **b)
# c = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Dans Python 3.9, vous pouvez ajouter deux dict en utilisant union | opérateur

# use the merging operator |
c = a | b
# c = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

5
Pour info: ce n'est pas une syntaxe valide en Python 2.7
Asav Patel

2
Cette syntaxe est assez nouvelle (Python 3.5)
pianoJames

24
a.update(b)

Ajoutera les clés et les valeurs de b à a , écrasant s'il y a déjà une valeur pour une clé.


17

Comme d'autres l'ont mentionné, a.update(b)pour certains mourants a, vous bobtiendrez le résultat que vous avez demandé dans votre question. Cependant, je tiens à souligner que de nombreuses fois, j'ai vu la extendméthode de mappage / définition d'objets désirer que dans la syntaxe a.extend(b), ales valeurs de '' NE soient PAS écrasées par bles valeurs de ''. a.update(b)écrase ales valeurs de, et n'est donc pas un bon choix pour extend.

Notez que certaines langues appellent cette méthode defaultsou inject, comme cela peut être considéré comme un moyen d'injecter les valeurs de b (qui pourraient être un ensemble de valeurs par défaut) dans un dictionnaire sans écraser les valeurs qui pourraient déjà exister.

Bien sûr, vous pouvez simplement noter que a.extend(b)c'est presque la même chose que b.update(a); a=b. Pour supprimer l'affectation, vous pouvez le faire ainsi:

def extend(a,b):
    """Create a new dictionary with a's properties extended by b,
    without overwriting.

    >>> extend({'a':1,'b':2},{'b':3,'c':4})
    {'a': 1, 'c': 4, 'b': 2}
    """
    return dict(b,**a)

Merci à Tom Leys pour cette idée intelligente en utilisant un dictconstructeur sans effet secondaire pour extend.


1

Vous pouvez également utiliser les collections de python.Chainmap qui a été introduit dans python 3.3.

from collections import Chainmap
c = Chainmap(a, b)
c['a'] # returns 1

Cela présente quelques avantages possibles, selon votre cas d'utilisation. Ils sont expliqués plus en détail ici , mais je vais donner un bref aperçu:

  • Un plan de chaîne utilise uniquement des vues des dictionnaires, donc aucune donnée n'est réellement copiée. Il en résulte un chaînage plus rapide (mais une recherche plus lente)
  • Aucune clé n'est réellement remplacée, si nécessaire, vous savez si les données proviennent de a ou b.

Cela le rend principalement utile pour des choses comme les dictionnaires de configuration.


0

Si vous en avez besoin en tant que classe , vous pouvez l'étendre avec dict et utiliser la méthode de mise à jour :

Class a(dict):
  # some stuff
  self.update(b)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.