two - récupérer clé dictionnaire python




Comment fusionner deux dictionnaires en une seule expression? (20)

Comment fusionner deux dictionnaires Python dans une seule expression?

Pour les dictionnaires x et y , z devient un dictionnaire peu fusionné, les valeurs de y remplaçant celles de x .

  • En Python 3.5 ou supérieur:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • En Python 2, (ou 3.4 ou inférieur) écrivez une fonction:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    

    et maintenant:

    z = merge_two_dicts(x, y)
    

Explication

Supposons que vous avez deux dictionnaires et que vous souhaitez les fusionner dans un nouveau dict sans modifier les dictés d'origine:

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

Le résultat souhaité est d'obtenir un nouveau dictionnaire ( z ) avec les valeurs fusionnées et les valeurs du second dict écrasant celles du premier.

>>> z
{'a': 1, 'b': 3, 'c': 4}

Une nouvelle syntaxe pour cela, proposée dans PEP 448 et disponible à partir de Python 3.5 , est

z = {**x, **y}

Et c'est bien une expression unique.

Notez que nous pouvons également fusionner avec la notation littérale:

z = x.copy()
z.update(y) # which returns None since it mutates z

et maintenant:

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

Il montre maintenant qu’il a été mis en œuvre dans le calendrier de publication de la version 3.5, PEP 478 , et qu’il a été intégré dans le document What's New in Python 3.5 .

Toutefois, étant donné que de nombreuses organisations utilisent encore Python 2, vous pouvez le faire de manière rétrocompatible. La manière classique de Pythonic, disponible dans Python 2 et Python 3.0-3.4, consiste à effectuer cette opération en deux étapes:

z = merge_two_dicts(x, y)

Dans les deux approches, y viendra en second et ses valeurs remplaceront les valeurs de x , ainsi 'b' désignera 3 dans notre résultat final.

Pas encore sur Python 3.5, mais je veux une seule expression

Si vous n'êtes pas encore sur Python 3.5, ou si vous avez besoin d'écrire du code compatible avec les versions antérieures et que vous voulez ceci dans une seule expression , la méthode la plus performante pour une approche correcte consiste à l'insérer dans une fonction:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

et puis vous avez une seule expression:

z = merge_dicts(a, b, c, d, e, f, g) 

Vous pouvez également créer une fonction pour fusionner un nombre indéterminé de dict, de zéro à un très grand nombre:

z = dict(x.items() + y.items())

Cette fonction fonctionnera en Python 2 et 3 pour tous les dict. par exemple, les chiffres donnés de a à g :

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

et les paires valeur / clé en g auront priorité sur les repères a à f , et ainsi de suite.

Critiques d'autres réponses

N'utilisez pas ce que vous voyez dans la réponse précédemment acceptée:

>>> c = dict(a.items() | b.items())

Dans Python 2, vous créez deux listes en mémoire pour chaque dict, vous créez une troisième liste en mémoire de longueur égale à la longueur des deux premières assemblées, puis vous supprimez les trois listes pour créer le dict. En Python 3, cela échouera car vous ajoutez deux objets dict_items ensemble, pas deux listes -

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

et vous devez les créer explicitement sous forme de listes, par exemple z = dict(list(x.items()) + list(y.items())) . C'est un gaspillage de ressources et de puissance de calcul.

De même, prendre l'union des items() dans Python 3 ( viewitems() dans Python 2.7) échouera également lorsque les valeurs sont des objets non obligatoires (comme des listes, par exemple). Même si vos valeurs sont hashable, étant donné que les ensembles sont non ordonnés sémantiquement, le comportement n'est pas défini en ce qui concerne la priorité. Alors ne fais pas ça:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

Cet exemple montre ce qui se passe lorsque les valeurs sont inshassables:

z = dict(x, **y)

Voici un exemple où y devrait avoir la priorité, mais la valeur de x est conservée en raison de l'ordre arbitraire des ensembles:

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

Un autre hack que vous ne devriez pas utiliser:

dict(a=1, b=10, c=11)

Ceci utilise le constructeur de dict , et est très rapide et efficace en mémoire (même légèrement plus que notre processus en deux étapes), mais à moins que vous ne sachiez exactement ce qui se passe ici (c'est-à-dire que le second dict est transmis en tant qu'argument de mot clé au dict constructeur), il est difficile à lire, ce n’est pas l’utilisation prévue, et donc ce n’est pas Pythonic.

Voici un exemple d'utilisation corrigée dans Django .

Les dict sont conçus pour prendre des clés obligatoires (par exemple, frozensets ou tuples), mais cette méthode échoue en Python 3 lorsque les clés ne sont pas des chaînes.

{'a': 1, 'b': 10, 'c': 11}

Sur la liste de diffusion , Guido van Rossum, le créateur du langage, a écrit:

Je suis d'accord pour déclarer dict ({}, ** {1: 3}) illégal, car il s'agit d'un abus du mécanisme **.

et

Apparemment, dict (x, ** y) se présente comme "cool hack" pour "call x.update (y) and return x". Personnellement, je le trouve plus ignoble que cool.

Je crois comprendre (ainsi que le créateur du langage ) que l'utilisation prévue de dict(**y) sert à créer des dict à des fins de lisibilité, par exemple:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

au lieu de

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

Réponse aux commentaires

Malgré ce que dit Guido, dict(x, **y) est conforme à la spécification dict, qui est d'ailleurs. fonctionne à la fois pour Python 2 et 3. Le fait que cela ne fonctionne que pour les clés de chaîne est une conséquence directe du fonctionnement des paramètres de mot-clé et non un raccourci de dict. L'utilisation de l'opérateur ** à cet endroit n'est pas non plus un abus du mécanisme. En fait, ** a été conçu précisément pour transmettre les mots-clés en tant que mots-clés.

Encore une fois, cela ne fonctionne pas pour 3 lorsque les clés sont des chaînes. Le contrat d'appel implicite stipule que les espaces de noms prennent des caractères ordinaires, tandis que les utilisateurs ne doivent transmettre que des arguments de mots clés qui sont des chaînes. Tous les autres callables l'ont appliqué. dict brisé cette cohérence dans Python 2:

dict((k, v) for d in dicts for k, v in d.items())

Cette incohérence était mauvaise compte tenu des autres implémentations de Python (Pypy, Jython, IronPython). Ainsi, il a été corrigé dans Python 3, car cet usage pourrait constituer un changement radical.

Je vous soumets qu'il s'agit d'une incompétence malveillante pour écrire intentionnellement un code qui ne fonctionne que dans une version d'un langage ou qui ne fonctionne que compte tenu de certaines contraintes arbitraires.

Plus de commentaires:

dict(x.items() + y.items()) reste la solution la plus lisible pour Python 2. La lisibilité compte.

Ma réponse: merge_two_dicts(x, y) me semble beaucoup plus clair, si nous nous préoccupons de la lisibilité. Et il n’est pas compatible en aval, car Python 2 est de plus en plus déconseillé.

{**x, **y} ne semble pas gérer les dictionnaires imbriqués. le contenu des clés imbriquées est simplement écrasé, pas fusionné [...] J'ai fini par être brûlé par ces réponses qui ne se confondent pas de manière récursive et j'ai été surpris que personne ne l'ait mentionné. Dans mon interprétation du mot "fusion", ces réponses décrivent "la mise à jour d'un dict avec un autre" et non la fusion.

Oui. Je dois vous renvoyer à la question, qui demande une fusion peu profonde de deux dictionnaires, les valeurs du premier étant écrasées par celles du second - en une seule expression.

En supposant deux dictionnaires de dictionnaires, on pourrait les fusionner de manière récursive en une seule fonction, mais vous devriez faire attention à ne pas modifier les dictés de l'une ou l'autre source, et le moyen le plus sûr d'éviter cela est de faire une copie lors de l'attribution de valeurs. Comme les clés doivent être lavables et sont donc généralement immuables, il est inutile de les copier:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

Usage:

import timeit

Poser des contingences pour d’autres types de valeurs va bien au-delà de la portée de cette question. Je vais donc vous indiquer ma réponse à la question canonique de «Dictionnaires de dictionnaires fusionnés» .

Ad-hocs moins performant mais correct

Ces approches sont moins performantes, mais elles fourniront un comportement correct. Ils seront beaucoup moins performants que la copy et la update ou la nouvelle décompression car ils parcourent chaque paire clé-valeur à un niveau d'abstraction supérieur, mais ils respectent l'ordre de priorité (ces derniers sont prioritaires).

Vous pouvez également chaîner les dicts manuellement dans une compréhension de dictée:

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

ou en python 2.6 (et peut-être dès 2.4 lorsque des expressions de générateur ont été introduites):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

itertools.chain enchaînera les itérateurs sur les paires clé-valeur dans le bon ordre:

z = dict(x.items() + y.items())

Analyse de performance

Je ne ferai que l'analyse de la performance des usages connus pour se comporter correctement.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

Ce qui suit est fait sur Ubuntu 14.04

En Python 2.7 (Python système):

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

En Python 3.5 (PPA Deadsnakes):

z = x.copy()
z.update(y)

Ressources sur les dictionnaires

J'ai deux dictionnaires Python et je veux écrire une seule expression qui renvoie ces deux dictionnaires, fusionnés. La méthode update() serait ce dont j'avais besoin si elle retournait son résultat au lieu de modifier un dict sur place.

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

Comment puis-je obtenir le dict final fusionné dans z , pas x ?

(Pour être très clair, je cherche aussi le traitement des dict.update() de dict.update() ).


Mise à jour récursive / profonde d'un dict

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

Manifestation:

final = {'a': 1, 'b': 1, **x, **y}

Les sorties:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

Merci Rednaw pour les modifications.


Bien que la question ait déjà reçu plusieurs réponses, cette solution simple au problème n'a pas encore été listée.

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

C'est aussi rapide que z0 et le mal z2 mentionné ci-dessus, mais facile à comprendre et à changer.


Ce ne sera probablement pas une réponse populaire, mais vous ne voudrez certainement pas faire cela. Si vous voulez une copie fusionnée, utilisez copy (ou deepcopy , selon ce que vous voulez), puis mettez à jour. Les deux lignes de code sont beaucoup plus lisibles - plus de Pythonic - que la création d’une seule ligne avec .items () + .items (). Explicite est meilleur qu'implicite.

De plus, lorsque vous utilisez .items () (avant Python 3.0), vous créez une nouvelle liste contenant les éléments du dict. Si vos dictionnaires sont volumineux, il en résulte beaucoup de frais généraux (deux listes volumineuses qui seront supprimées dès que le dict fusionné sera créé). update () peut fonctionner plus efficacement, car il peut parcourir la deuxième phrase, article par article.

En terme de time :

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

OMI le petit ralentissement entre les deux premiers en vaut la peine pour la lisibilité. De plus, les arguments de mot-clé pour la création de dictionnaire n’ont été ajoutés qu’en Python 2.3, alors que copy () et update () fonctionnent dans les versions antérieures.


Dans votre cas, vous pouvez faire ce qui suit:

z = dict(x, **y)

Comme vous le souhaitez, cela mettra le dernier dict en z et fera en sorte que la valeur de la touche b soit correctement remplacée par la valeur du second ( y ) dict:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

Si vous utilisez Python 3, c'est un peu plus compliqué. Pour créer z :

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

En Python 3, vous pouvez utiliser collections.ChainMap qui regroupe plusieurs dict ou autres mappages pour créer une seule vue pouvant être mise à jour:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

Je voulais quelque chose de similaire, mais avec la possibilité de spécifier la manière dont les valeurs des clés en double ont été fusionnées, j'ai donc piraté cela (mais je ne l'ai pas lourdement testé). Évidemment, il ne s'agit pas d'une expression unique, mais d'un appel de fonction unique.

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

La meilleure version à laquelle je puisse penser sans utiliser de copie serait:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

C'est plus rapide que dict(x.items() + y.items()) mais pas aussi vite que n = copy(a); n.update(b) n = copy(a); n.update(b) , au moins sur CPython. Cette version fonctionne également en Python 3 si vous changez iteritems() en items() , ce qui est automatiquement effectué par l'outil 2to3.

Personnellement, j'aime mieux cette version car elle décrit assez bien ce que je veux dans une seule syntaxe fonctionnelle. Le seul problème mineur est qu'il n'est pas évident que les valeurs de y l'emportent sur celles de x, mais je ne crois pas qu'il soit difficile de comprendre cela.


Si vous pensez que les lambdas sont diaboliques, alors ne lisez pas plus loin. Si vous le souhaitez, vous pouvez écrire la solution rapide et économe en mémoire avec une expression:

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

Comme suggéré ci-dessus, utiliser deux lignes ou écrire une fonction est probablement une meilleure solution.


Solution simple utilisant itertools qui préserve l’ordre (ces derniers sont prioritaires)

import collections
a = {1: 1, 2: 2}
b = {2: 3, 3: 4}
c = {3: 5}

r = dict(collections.ChainMap(a, b, c))
print(r)

Et c'est usage:

{1: 1, 2: 2, 3: 4}

Une alternative:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

Une autre option plus concise:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

Remarque : cela est devenu une réponse populaire, mais il est important de souligner que si y a des clés autres que des chaînes, le fait que cela fonctionne est un abus des détails de l'implémentation CPython et que cela ne fonctionne pas dans Python 3. , ou dans PyPy, IronPython ou Jython. En outre, Guido n'est pas un fan . Donc, je ne peux pas recommander cette technique pour un code portable compatible avec les versions ultérieures ou à implémentation croisée, ce qui signifie vraiment qu'il devrait être entièrement évité.



Cela peut être fait avec une compréhension simple dict:

 >>> x = {'a':1, 'b': 2} >>> y = {'b':10, 'c': 11} >>> { key: y[key] if key in y else x[key] for key in set(x) + set(y) } 

À mon avis, la meilleure solution pour la partie "expression unique" étant donné qu'aucune fonction supplémentaire n'est nécessaire et qu'elle est courte.



Le problème que j'ai avec les solutions répertoriées à ce jour est que, dans le dictionnaire fusionné, la valeur de la clé "b" est 10, mais, à mon sens, elle devrait être de 12. À cet égard, je présente ce qui suit:

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

Résultats:

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}

Dans Python 3.5, vous pouvez utiliser unpack **pour créer un nouveau dictionnaire. Cette méthode n'a pas été montrée dans les réponses précédentes. En outre, il vaut mieux utiliser {}au lieu de dict(). Car {}est un littéral python et dict()implique un appel de fonction.

 dict1 = {'a':1} dict2 = {'b':2} new_dict = {**dict1, **dict2} >>>new_dict {'a':1, 'a':2} 

Même si les réponses étaient bonnes pour ce dictionnaire superficiel , aucune des méthodes définies ici ne fait une fusion de dictionnaire profond.

Des exemples suivent:

{'two': True, 'one': {'extra': False}}

On pourrait s'attendre à un résultat de quelque chose comme ceci:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items()+y.items())
print(z)

Au lieu de cela, nous obtenons ceci:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items()|y.items())
print(z)

L'entrée 'one' aurait dû avoir 'profondeur_2' et 'extra' dans son dictionnaire s'il s'agissait vraiment d'une fusion.

Utiliser la chaîne aussi ne fonctionne pas:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

Résulte en:

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

La fusion profonde que rcwesick a donnée crée également le même résultat.

Oui, la fusion des exemples de dictionnaires fonctionnera, mais aucun d’entre eux n’est un mécanisme générique à fusionner. Je mettrai cela à jour plus tard une fois que j'aurai écrit une méthode qui effectue une véritable fusion.


from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

Parmi ces réponses douteuses et douteuses, cet exemple éclatant est le seul et unique moyen de fusionner des dictionnaires en Python, avalisé par le dictateur à vie Guido van Rossum lui-même! Quelqu'un a suggéré la moitié de cela, mais ne l'a pas mise dans une fonction.

dict(x.items() | y.items())

donne:

dict(x.viewitems() | y.viewitems())






merge