Comment fusionner deux dictionnaires en une seule expression?

j'ai deux dictionnaires Python, et je veux écrire une seule expression qui renvoie ces deux dictionnaires, ont fusionné. La méthode update() serait ce dont j'ai besoin, si elle retournait son résultat au lieu de modifier un dict en 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 ce dict final fusionné dans z , pas x ?

(pour être plus clair, le dernier-un-gagne la gestion des conflits de dict.update() est ce que je cherche aussi.)

3355
demandé sur wim 2008-09-02 11:44:30

30 réponses

comment fusionner deux dictionnaires Python en une seule expression?

Pour les dictionnaires x et y , z devient une fusion de dictionnaire avec des valeurs de y remplace ceux de x .

  • en Python 3.5 ou plus,:

    z = {**x, **y}
    w = {'foo': 'bar', 'baz': 'qux', **y}  # merge a dict with literal values
    
  • en Python 2, (ou 3.4 ou inférieur) de la 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

    z = merge_two_dicts(x, y)
    

explication

dire que vous avez deux dicts et vous voulez les fusionner dans un nouveau dict sans modifier les dicts 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é dans PEP 448 et disponible à partir de Python 3.5 , est

z = {**x, **y}

et c'est en effet une seule expression. Il est maintenant affiché comme mis en œuvre dans le calendrier de publication pour 3.5, PEP 478 , et il a maintenant fait son chemin dans Quoi de neuf en Python 3.5 document.

cependant, étant donné que de nombreuses organisations sont toujours sur Python 2, vous pouvez le faire dans un rétro-compatible. La méthode classique de Python, disponible en Python 2 et Python 3.0-3.4, est de le faire en deux étapes:

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

dans les deux approches, y viendra en second et ses valeurs remplaceront x valeurs de s, donc 'b' pointera vers 3 dans notre résultat final.

pas encore sur Python 3.5, mais je veux une expression unique

si vous n'êtes pas encore sur Python 3.5, ou si vous avez besoin d'écrire du code rétrocompatible, et que vous voulez cela dans une simple expression , la plus performante tout en ayant une approche correcte est de le mettre dans une fonction:

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

et puis vous avez une seule expression:

z = merge_two_dicts(x, y)

, Vous pouvez aussi faire une fonction pour fusionner un nombre indéterminé de dicts, de zéro à un très grand nombre:

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

cette fonction fonctionnera en Python 2 et 3 pour tous les dicts. par exemple dicts a à g :

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

et les paires de valeurs clés dans g auront priorité sur les dicts a à f , et ainsi de suite.

Critiques d'autres réponses

de Ne pas utiliser ce que vous voyez dans l'ancien accepté de répondre:

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

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

>>> 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 vous devez les créer explicitement en tant que listes, par exemple z = dict(list(x.items()) + list(y.items())) . C'est un gaspillage de ressources et de la puissance de calcul.

de la même façon, prendre l'union de items() en Python 3 ( viewitems() en python 2.7) échouera aussi quand les valeurs sont des objets indéchiffrables (comme des listes, par exemple). Même si vos valeurs sont hachables, puisque les ensembles ne sont pas classés sémantiquement, le comportement n'est pas défini en ce qui concerne la priorité. Alors ne faites pas ça:

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

cet exemple montre ce qui se passe lorsque les valeurs sont unhashable:

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

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

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

un Autre hack vous ne devez pas utiliser:

z = dict(x, **y)

cela utilise le constructeur dict , et est très rapide et efficace en mémoire (même un peu plus-donc que notre processus en deux étapes), mais à moins que vous sachiez exactement ce qui se passe ici (que is, le second dict est passé comme argument de mots-clés au constructeur de dict), il est difficile à lire, ce n'est pas l'usage prévu, et donc ce n'est pas Pythonic.

voici un exemple de l'usage étant remédié dans django .

les Dicts sont destinés à prendre des clés hachables (par exemple frozensets ou tuples), mais cette méthode échoue en Python 3 lorsque les clés ne sont pas des chaînes.

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

de la mailing list , Guido van Rossum, le créateur de la langue, a écrit:

je suis d'accord avec déclaration DCT({}, **{1:3}) illégal, car après tout, il est d'abus de mécanisme.

et

apparemment dict (x, **y) se promène comme "cool hack" pour "call" x.mise à jour(y) et retour x". Personnellement, je trouve que c' de plus détestable que cool.

je crois comprendre (ainsi que la compréhension du créateur de la langue ) que l'usage prévu pour dict(**y) est pour créer des dicts à des fins de lisibilité, par exemple:

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

au lieu de

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

réponse aux commentaires

malgré ce que dit Guido, dict(x, **y) est en ligne avec la spécification dict, qui btw. fonctionne pour Python 2 et 3. Le fait que cela ne fonctionne que pour les clés string est une conséquence directe de la façon dont les paramètres des mots clés fonctionnent et non un raccourcissement de dict. L'utilisation de l'opérateur ** dans cet endroit n'est pas non plus un abus du mécanisme, en fait ** a été conçu précisément pour passer des dicts comme mots clés.

encore une fois, il ne fonctionne pas pour 3 lorsque les clés sont non-cordes. Le contrat d'appel implicite est que les espaces de noms prennent dicts ordinaires, tandis que les utilisateurs doivent seulement passer des arguments de mot clé qui sont des chaînes. Tous les autres callables l'ont fait respecter. dict a cassé cette consistance en Python 2:

>>> 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}

cette incohérence était mauvaise compte tenu des autres implémentations de Python (Pypy, Jython, IronPython). Ainsi, il a été corrigé en Python 3, car cette utilisation pourrait être un changement de rupture.

je vous soumets que c'est une incompétence malveillante d'écrire intentionnellement du code qui ne fonctionne dans une version d'une langue ou qui ne fonctionne que moyennant certaines contraintes arbitraires.

un autre commentaire:

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

ma réponse: merge_two_dicts(x, y) me semble en fait beaucoup plus claire, si nous sommes réellement préoccupés par la lisibilité. Et il n'est pas compatible forward, car Python 2 est de plus en plus déprécié.

moins Performant mais Correct Ad-hoc

ces approches sont moins performantes, mais elles fourniront un comportement correct. Ils seront beaucoup moins performants que copy et update ou le nouveau déballage parce qu'ils itèrent à travers chaque paire de valeur clé à un niveau plus élevé d'abstraction, mais ils do respectent l'ordre de priorité (ces derniers dicts ont priorité)

vous pouvez également enchaîner les dicts manuellement à l'intérieur d'une compréhension dict:

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

ou en python 2.6 (et peut-être aussi tôt que 2.4 lorsque les expressions de générateur ont été introduites):

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

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

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

Analyse De Performance

je vais seulement faire l'analyse de performance de la des usages connus pour se comporter correctement.

import timeit

ce qui suit est fait sur Ubuntu 14.04

en Python 2.7 (système Python):

>>> 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

en Python 3.5 (deadsnakes PPA):

>>> 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

ressources sur les dictionnaires

3562
répondu Aaron Hall 2018-07-03 17:02:21

Dans votre cas, ce que vous pouvez faire est:

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

cela mettra, comme vous le voulez , le dict final dans z , et fera la valeur pour la touche b être correctement dépassé par la seconde ( y ) valeur de dict:

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

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

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}
1456
répondu Thomas Vander Stichele 2018-08-29 17:18:02

une alternative:

z = x.copy()
z.update(y)
559
répondu Matthew Schinckel 2008-09-02 13:00:46

une autre option, plus concise:

z = dict(x, **y)

Note : ceci est devenu une réponse populaire, mais il est important de souligner que si y a des clés non-string, le fait que cela fonctionne du tout est un abus d'un détail d'implémentation de CPython, et cela ne fonctionne pas en Python 3, ou en PyPy, IronPython, ou Jython. Aussi, Guido N'est pas un ventilateur . Je ne peux donc pas recommander cette technique pour cross-implementation portable code, ce qui signifie vraiment qu'il doit être évité entièrement.

278
répondu Carl Meyer 2016-01-21 06:43:24

ce ne sera probablement pas une réponse populaire, mais vous ne voulez presque certainement pas faire cela. Si vous voulez une copie qui est une fusion, puis utiliser la copie (ou deepcopy , selon ce que vous voulez) et puis mettre à jour. Les deux lignes de code sont beaucoup plus lisibles - plus Pythoniques - que la création d'une seule ligne .article. + )(article.)( Explicite est mieux qu'implicites.

En outre, lorsque vous utilisez .items () (pré Python 3.0), vous créez un nouveau liste qui contient les éléments du dict. Si vos dictionnaires sont grands, c'est beaucoup de frais généraux (deux grandes listes qui seront jetées dès que le dict fusionné sera créé). update () peut fonctionner de manière plus efficace, car il peut être exécuté à travers le second élément de dict-by-item.

En termes de temps :

>>> 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

IMO le léger ralentissement entre les deux premiers vaut la peine pour la lisibilité. Outre, les arguments de mots-clés pour la création de dictionnaires n'ont été ajoutés que dans Python 2.3, alors que copy() et update() fonctionneront dans les versions plus anciennes.

171
répondu Tony Meyer 2014-08-05 23:56:02

dans une réponse de suivi, vous avez posé des questions sur la performance relative de ces deux alternatives:

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

sur ma machine, au moins (un x86_64 assez ordinaire en Python 2.5.2), l'alternative z2 est non seulement plus courte et plus simple, mais aussi beaucoup plus rapide. Vous pouvez le vérifier vous-même en utilisant le module timeit fourni avec Python.

exemple 1: dictionnaires identiques cartographiant 20 entiers consécutifs pour eux-mêmes:

% 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

z2 gagne par un facteur de 3,5 ou. Des dictionnaires différents semblent donner des résultats tout à fait différents, mais z2 semble toujours arriver en tête. (Si vous obtenez des résultats incohérents pour le même test, essayer de passer dans -r avec un nombre plus grand que la valeur par défaut 3.)

exemple 2: dictionnaires ne se chevauchant pas mapping 252 short strings to integer et vice versa:

% 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

z2 gagne par un facteur d'environ 10. C'est une assez grande victoire, dans mon livre!

après avoir comparé ces deux-là, je me suis demandé si la mauvaise performance de z1 pourrait être attribuée à la surcharge de construire les deux listes d'Articles, ce qui à son tour m'a conduit à me demander si cette variation pourrait mieux fonctionner:

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

quelques essais rapides, p.ex.

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

conduisez-moi à conclure que z3 est un peu plus rapide que z1 , mais pas aussi rapide que z2 . Certainement pas la peine tout le taper.

cette discussion manque toujours quelque chose d'important, qui est une comparaison de performance de ces alternatives avec la manière" évidente "de fusionner deux listes: en utilisant la méthode update . Pour essayer de garder les choses sur un pied d'égalité avec les expressions, aucune de modifier x ou y, je vais faire une copie de x au lieu de le modifier en place, comme suit:

z0 = dict(x)
z0.update(y)

un résultat typique:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

en d'autres termes, z0 et z2 semblent avoir des performances essentiellement identiques. Pensez-vous que cela pourrait être une coïncidence? Je n'ai pas....

En fait, j'irais même jusqu'à prétendre qu'il est impossible pour un pur code Python pour faire mieux que cela. Et si vous pouvez faire beaucoup mieux dans une extension C module, j'imagine que les gens de Python pourraient bien être intéressés à incorporer votre code (ou une variation sur votre approche) dans le noyau Python. Python utilise dict dans beaucoup d'endroits; optimiser ses opérations est important.

vous pouvez aussi écrire ceci comme

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

que Tony n', mais (sans surprise) la différence de notation s'avère n'avoir aucun effet mesurable sur la performance. Utilisation selon la regarde droit pour vous. De bien sûr, il a tout à fait raison de souligner que la version à deux énoncés est beaucoup plus facile à comprendre.

118
répondu zaphod 2015-01-10 02:32:55

je voulais quelque chose de similaire, mais avec la possibilité de spécifier comment les valeurs sur les clés dupliquées ont été fusionnées, donc je l'ai hacké (mais je ne l'ai pas testé lourdement). Évidemment, ce n'est pas une simple expression, mais c'est un seul appel de fonction.

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result
86
répondu rcreswick 2014-09-13 19:56:21

en Python 3, vous pouvez utiliser collections.ChainMap qui regroupe plusieurs dicts ou d'autres mappages pour créer une vue unique, pouvant être mise à jour:

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = ChainMap({}, y, x)
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11
77
répondu Raymond Hettinger 2014-09-27 08:12:41

Récursivement/deep mise à jour d'un dict

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

démonstration:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

sorties:

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

Merci rednaw pour les modifications.

61
répondu Stan 2015-12-18 11:19:15

la meilleure version que je pourrais penser tout en n'utilisant pas la copie serait:

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

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

personnellement, je préfère cette version parce qu'elle décrit assez bien ce que je veux dans une seule syntaxe fonctionnelle. Le seul problème mineur est qu'il ne soit pas tout à fait évident que les valeurs de y l'emportent sur les valeurs de x, mais je ne crois pas qu'il soit difficile de le comprendre.

54
répondu driax 2010-10-14 18:55:15

Python 3.5 (PEP 448) permet une option de syntaxe plus agréable:

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

Ou même

final = {'a': 1, 'b': 1, **x, **y}
42
répondu Bilal Syed Hussain 2015-02-26 21:27:52
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

pour les éléments avec des clés dans les deux dictionnaires ('b'), vous pouvez contrôler lequel se retrouve dans la sortie en mettant celui dernier.

40
répondu Greg Hewgill 2008-09-02 07:49:27

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

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

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

35
répondu phobie 2011-10-14 16:12:33
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

parmi ces réponses obscures et douteuses, ce brillant exemple est le seul et unique bon moyen de fusionner les dicts en Python, approuvé par dictateur pour la vie Guido van Rossum lui-même! Quelqu'un d'autre a suggéré la moitié de ceci, mais ne l'a pas mis dans une fonction.

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

donne:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
33
répondu Sam Watkins 2012-08-06 09:30:07

si vous pensez que les lambdas sont mauvais alors ne lisez plus. Comme demandé, Vous pouvez écrire la solution rapide et efficace en mémoire avec une expression:

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}

comme suggéré ci-dessus, utiliser deux lignes ou écrire une fonction est probablement une meilleure façon de procéder.

25
répondu EMS 2011-11-23 18:20:48

soit pythonique. Utiliser un compréhension :

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

>>> print z
{'a': 1, 'c': 11, 'b': 10}
20
répondu Robino 2016-09-29 10:45:25

en python3, la" méthode 151930920 " ne renvoie plus une liste , mais plutôt une view , qui agit comme un ensemble. Dans ce cas, vous devrez prendre le set union depuis concaténation avec + ne fonctionnera pas:

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

pour les comportements de type python3 dans la version 2.7, la méthode viewitems devrait fonctionner à la place de items :

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

je préfère de toute façon cette notation puisqu'il semble plus naturel de penser à lui comme une opération de syndicat fixée plutôt que concaténation (comme le montre le titre).

Edit:

encore quelques points pour python 3. Tout d'abord, notez que le truc dict(x, **y) ne fonctionnera pas en python 3 à moins que les clés de y ne soient des cordes.

aussi, Chainmap de Raymond Hettinger answer est assez élégant, car il peut prendre un arbitraire nombre de dicts comme arguments, mais de la docs il semble qu'il regarde séquentiellement à travers une liste de tous les dicts pour chaque recherche:

recherches rechercher les correspondances sous-jacentes successivement jusqu'à ce qu'une clé soit trouvée.

Cela peut vous ralentir si vous avez beaucoup de recherches dans votre application:

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

Donc environ un ordre de grandeur plus lent pour les recherches. Je suis un fan de Chainmap, mais semble moins pratique là où il peut y avoir de nombreuses recherches.

19
répondu beardc 2017-05-23 12:34:53

abus conduisant à une solution d'une expression pour la réponse de Matthieu :

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

vous avez dit que vous vouliez une expression, Donc j'ai abusé de lambda pour lier un nom, et tuples pour outrepasser la limite d'une expression de lambda. Hésitez pas à grincer des dents.

vous pouvez aussi le faire bien sûr si vous ne vous souciez pas de le copier:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}
14
répondu Claudiu 2017-05-23 12:34:53

solution Simple à l'aide de itertools qui préserve l'ordre (ci dicts ont priorité)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

et son usage:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}
13
répondu reubano 2016-09-06 11:30:16

deux dictionnaires

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n dictionnaires

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum a de mauvaises performances. Voir https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python /

11
répondu Mathieu Larose 2016-10-02 18:16:17

en python 3:

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

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

:

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

Docs: https://docs.python.org/3/library/collections.html#collections.ChainMap :

11
répondu Skyduy 2017-05-24 07:24:50

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

exemples:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

on s'attendrait à un résultat de quelque chose comme ceci:

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

au lieu de cela, nous obtenons ceci:

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

l'entrée "one" aurait dû avoir "depth_2" et "extra" comme éléments à l'intérieur de son dictionnaire si c'était vraiment une fusion.

utilisant la chaîne aussi, ne fonctionne pas:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

résultats dans:

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

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

Oui, il va fonctionner pour fusionner les dictionnaires d'exemple, mais aucun d'eux ne sont un mécanisme générique pour fusionner. Je le mettrai à jour plus tard une fois que j'aurai écrit une méthode qui fait une vraie Fusion.

9
répondu Thanh Lim 2012-08-03 23:36:50

Pour Python 2:

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

Pour Python 3:

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

il donne la sortie: {'a': 1, 'c': 11, 'b': 10}

8
répondu Kalpesh Dusane 2016-09-01 06:29:01

puisant dans les idées d'ici et d'ailleurs, j'ai compris une fonction:

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

utilisation (testé en python 3):

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

vous pourriez utiliser un lambda à la place.

7
répondu Bijou Trouvaille 2013-07-19 05:49:19
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}
5
répondu John La Rooy 2013-11-13 10:01:31

le problème que j'ai avec les solutions énumérées à ce jour est que, dans le dictionnaire fusionné, la valeur de la touche" b " est 10 mais, à ma façon de penser, il devrait être 12. Dans cette perspective, je présente ce qui suit:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

Résultats:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}
5
répondu upandacross 2013-12-03 18:11:54

cela peut être fait avec une seule compréhension 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 réponse pour la partie 'single expression' car aucune fonction supplémentaire n'est nécessaire, et il est court.

5
répondu RemcoGerlich 2015-07-17 14:47:23
from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

cela devrait résoudre votre problème.

5
répondu reetesh11 2015-11-30 13:04:00

(Pour Python2.7* seulement; il existe des solutions plus simples pour Python3*.)

si vous n'êtes pas opposé à l'importation d'un module de bibliothèque standard, vous pouvez le faire

from functools import reduce

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

(le bit or a dans le lambda est nécessaire parce que dict.update retourne toujours None sur le succès.)

5
répondu kjo 2016-03-28 13:13:27

en Python 3.5 vous pouvez utiliser unpack ** afin de créer un nouveau dictionnaire. Cette méthode n'a pas été montrée dans les réponses précédentes. De plus, il est préférable d'utiliser {} au lieu de dict() . Parce que {} est un python littéral et dict() implique un appel de fonction.

dict1 = {'a':1}
dict2 = {'b':2}
new_dict = {**dict1, **dict2}
>>>new_dict
{'a':1, 'a':2}
5
répondu levi 2016-09-28 00:33:55