Frage

Ich habe zwei Python-Wörterbücher und möchte einen einzelnen Ausdruck schreiben, der diese beiden Wörterbücher zusammengeführt zurückgibt.Der update() Methode wäre das, was ich brauche, wenn sie ihr Ergebnis zurückgibt, anstatt ein Diktat direkt zu ändern.

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

Wie kann ich das endgültige zusammengeführte Diktat einbinden? z, nicht x?

(Um es ganz klar zu sagen: Der Letzte gewinnt bei der Konfliktbearbeitung dict.update() ist das, wonach ich auch suche.)

War es hilfreich?

Lösung

Wie kann ich zwei Python-Wörterbücher in einem einzigen Ausdruck zusammenführen?

Für Wörterbücher x Und y, z wird zu einem flach zusammengeführten Wörterbuch mit Werten aus y Ersetzen Sie diese durch x.

  • In Python 3.5 oder höher:

    z = {**x, **y}
    
  • Schreiben Sie in Python 2 (oder 3.4 oder niedriger) eine Funktion:

    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
    

    und nun:

    z = merge_two_dicts(x, y)
    

Erläuterung

Angenommen, Sie haben zwei Diktate und möchten diese zu einem neuen Diktat zusammenführen, ohne die ursprünglichen Diktate zu ändern:

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

Das gewünschte Ergebnis besteht darin, ein neues Wörterbuch zu erhalten (z), wobei die Werte zusammengeführt werden und die Werte des zweiten Diktats diejenigen des ersten überschreiben.

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

Eine neue Syntax hierfür, vorgeschlagen in PEP 448 Und verfügbar ab Python 3.5, Ist

z = {**x, **y}

Und es ist tatsächlich ein einziger Ausdruck.

Beachten Sie, dass wir auch mit der wörtlichen Notation verschmelzen können:

z = {**x, 'foo': 1, 'bar': 2, **y}

und nun:

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

Es wird jetzt als implementiert angezeigt Veröffentlichungsplan für 3.5, PEP 478, und es hat jetzt Einzug gehalten Was ist neu in Python 3.5? dokumentieren.

Da jedoch viele Organisationen immer noch Python 2 verwenden, möchten Sie dies möglicherweise auf eine abwärtskompatible Weise tun.Der klassische Pythonic-Weg, der in Python 2 und Python 3.0–3.4 verfügbar ist, besteht darin, dies in einem zweistufigen Prozess durchzuführen:

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

Bei beiden Ansätzen y wird an zweiter Stelle stehen und seine Werte werden ersetzt xDie Werte also 'b' wird darauf hinweisen 3 in unserem Endergebnis.

Noch nicht auf Python 3.5, möchte aber eines einzelner Ausdruck

Wenn Sie noch nicht mit Python 3.5 arbeiten oder abwärtskompatiblen Code schreiben müssen und dies in einem möchten einzelner Ausdruck, der leistungsstärkste und gleichzeitig korrekteste Ansatz besteht darin, ihn in eine Funktion einzufügen:

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

und dann haben Sie einen einzigen Ausdruck:

z = merge_two_dicts(x, y)

Sie können auch eine Funktion erstellen, um eine undefinierte Anzahl von Diktaten zusammenzuführen, von null bis zu einer sehr großen Zahl:

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

Diese Funktion funktioniert in Python 2 und 3 für alle Diktate.z.B.Diktate gegeben a Zu g:

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

und Schlüsselwertpaare in g wird Vorrang vor Diktaten haben a Zu f, und so weiter.

Kritik an anderen Antworten

Verwenden Sie nicht das, was Sie in der zuvor akzeptierten Antwort sehen:

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

In Python 2 erstellen Sie für jedes Diktat zwei Listen im Speicher, erstellen im Speicher eine dritte Liste mit einer Länge, die der Länge der ersten beiden zusammengenommen entspricht, und verwerfen dann alle drei Listen, um das Diktat zu erstellen. In Python 3 wird dies fehlschlagen weil du zwei hinzufügst dict_items Objekte zusammen, nicht zwei Listen -

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

und Sie müssten sie explizit als Listen erstellen, z. z = dict(list(x.items()) + list(y.items())).Dies ist eine Verschwendung von Ressourcen und Rechenleistung.

Ebenso nimmt man die Vereinigung von items() in Python 3 (viewitems() in Python 2.7) schlägt auch fehl, wenn es sich bei den Werten um nicht hashbare Objekte handelt (z. B. Listen).Auch wenn Ihre Werte hashbar sind, Da Mengen semantisch ungeordnet sind, ist das Verhalten hinsichtlich der Priorität undefiniert.Also tun Sie das nicht:

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

Dieses Beispiel zeigt, was passiert, wenn Werte nicht gehasht werden können:

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

Hier ist ein Beispiel, bei dem y Vorrang haben sollte, aber stattdessen der Wert von x aufgrund der willkürlichen Reihenfolge der Mengen beibehalten wird:

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

Ein weiterer Hack, den Sie nicht verwenden sollten:

z = dict(x, **y)

Dies nutzt die dict Konstruktor und ist sehr schnell und speichereffizient (sogar etwas mehr als unser zweistufiger Prozess), aber es sei denn, Sie wissen genau, was hier passiert (das heißt, das zweite Diktat wird als Schlüsselwortargumente an den Diktkonstruktor übergeben), Es ist schwer zu lesen, es ist nicht die beabsichtigte Verwendung und daher nicht pythonisch.

Hier ist ein Beispiel für die Verwendung in Django behoben.

Diktate sollen hashbare Schlüssel annehmen (z. B.Frozensets oder Tupel), aber Diese Methode schlägt in Python 3 fehl, wenn Schlüssel keine Zeichenfolgen sind.

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

Von dem Mailingliste, Guido van Rossum, der Schöpfer der Sprache, schrieb:

Mir geht es gut mit dem Deklarieren von Dict ({}, ** {1: 3}) illegal, da es schließlich den Missbrauch des ** Mechanismus ist.

Und

Anscheinend wird Dict (x, ** y) als "cooler Hack" für "Rufen Sie X.Update (y) und return x" herum.Persönlich finde ich es verabscheuungswürdiger als cool.

Es ist mein Verständnis (sowie das Verständnis der Schöpfer der Sprache), dass die beabsichtigte Verwendung für dict(**y) dient zum Erstellen von Diktaten aus Gründen der Lesbarkeit, z. B.:

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

anstatt

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

Antwort auf Kommentare

Ungeachtet dessen, was Guido sagt, dict(x, **y) stimmt mit der dict-Spezifikation überein, was übrigens der Fall ist.Funktioniert sowohl für Python 2 als auch für 3.Die Tatsache, dass dies nur für Zeichenfolgenschlüssel funktioniert, ist eine direkte Folge der Funktionsweise von Schlüsselwortparametern und keine Unterlassung von dict.Die Verwendung des **-Operators an dieser Stelle stellt auch keinen Missbrauch des Mechanismus dar, denn ** wurde tatsächlich speziell dafür entwickelt, Diktate als Schlüsselwörter zu übergeben.

Auch hier funktioniert es nicht für 3, wenn Schlüssel keine Zeichenfolgen sind.Der implizite Aufrufvertrag sieht vor, dass Namespaces gewöhnliche Diktate annehmen, während Benutzer nur Schlüsselwortargumente übergeben dürfen, die Zeichenfolgen sind.Alle anderen Callables haben dies durchgesetzt. dict hat diese Konsistenz in Python 2 gebrochen:

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

Diese Inkonsistenz war bei anderen Implementierungen von Python (Pypy, Jython, IronPython) schlimm.Daher wurde es in Python 3 behoben, da diese Verwendung eine bahnbrechende Änderung darstellen könnte.

Ich behaupte, dass es böswillige Inkompetenz ist, absichtlich Code zu schreiben, der nur in einer Version einer Sprache funktioniert oder der nur unter bestimmten willkürlichen Einschränkungen funktioniert.

Mehr Kommentare:

dict(x.items() + y.items()) ist immer noch die am besten lesbare Lösung für Python 2.Lesbarkeit zählt.

Meine Antwort: merge_two_dicts(x, y) scheint mir tatsächlich viel klarer zu sein, wenn es uns tatsächlich um die Lesbarkeit geht.Und es ist nicht aufwärtskompatibel, da Python 2 zunehmend veraltet ist.

{**x, **y} scheint verschachtelte Wörterbücher nicht zu verarbeiten.Der Inhalt verschachtelter Schlüssel wird einfach überschrieben und nicht zusammengeführt. [...] Diese Antworten, die nicht rekursiv zusammengeführt werden, haben mich am Ende verbrannt und ich war überrascht, dass niemand das erwähnt hat.In meiner Interpretation des Wortes „zusammenführen“ beschreiben diese Antworten „das Aktualisieren eines Diktats mit einem anderen“ und nicht das Zusammenführen.

Ja.Ich muss Sie noch einmal auf die Frage verweisen, bei der es um eine geht seicht Verschmelzung von zwei Wörterbücher, wobei die Werte des ersten durch die Werte des zweiten überschrieben werden – in einem einzigen Ausdruck.

Wenn man zwei Wörterbücher mit Wörterbüchern annimmt, könnte man sie rekursiv in einer einzigen Funktion zusammenführen, aber Sie sollten darauf achten, die Diktate aus keiner der Quellen zu ändern. Der sicherste Weg, dies zu vermeiden, besteht darin, beim Zuweisen von Werten eine Kopie zu erstellen.Da Schlüssel hashbar sein müssen und daher in der Regel unveränderlich sind, ist es sinnlos, sie zu kopieren:

from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

Verwendung:

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

Eventualverbindlichkeiten für andere Werttypen zu finden, würde den Rahmen dieser Frage bei weitem sprengen, daher möchte ich Sie darauf hinweisen meine Antwort auf die kanonische Frage zu „Wörterbücher von Wörterbüchern zusammenführen“.

Weniger performant, aber korrekte Ad-hocs

Diese Ansätze sind weniger leistungsfähig, sorgen aber für korrektes Verhalten.Sie werden viel weniger performanter als copy Und update oder das neue Entpacken, weil sie jedes Schlüssel-Wert-Paar auf einer höheren Abstraktionsebene durchlaufen, aber sie Tun Respektieren Sie die Rangfolge (letztere Vorschriften haben Vorrang)

Sie können die Diktate auch manuell innerhalb eines Diktatverständnisses verketten:

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

oder in Python 2.6 (und vielleicht schon 2.4, als Generatorausdrücke eingeführt wurden):

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

itertools.chain verkettet die Iteratoren über die Schlüssel-Wert-Paare in der richtigen Reihenfolge:

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

Leistungsanalyse

Ich werde die Leistungsanalyse nur der Verwendungen durchführen, von denen bekannt ist, dass sie sich korrekt verhalten.

import timeit

Das Folgende wird unter Ubuntu 14.04 durchgeführt

In Python 2.7 (System-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

In 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

Ressourcen zu Wörterbüchern

Andere Tipps

Was Sie in Ihrem Fall tun können, ist:

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

Dadurch wird, wie Sie es wünschen, das endgültige Diktat eingefügt z, und machen Sie den Wert für Schlüssel b ordnungsgemäß durch die Sekunde überschrieben werden (y) Wert des Diktats:

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

Wenn Sie Python 3 verwenden, ist es nur etwas komplizierter.Erschaffen z:

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

Eine Alternative:

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

Eine weitere, prägnantere Option:

z = dict(x, **y)

Notiz:Dies ist zu einer beliebten Antwort geworden, aber es ist wichtig darauf hinzuweisen, dass if y Hat irgendwelche Nicht-String-Schlüssel, ist die Tatsache, dass dies überhaupt funktioniert, ein Missbrauch eines CPython-Implementierungsdetails, und es funktioniert nicht in Python 3 oder in PyPy, IronPython oder Jython.Auch, Guido ist kein Fan.Daher kann ich diese Technik nicht für aufwärtskompatiblen oder implementiertenübergreifenden portablen Code empfehlen, was eigentlich bedeutet, dass sie gänzlich vermieden werden sollte.

Dies wird wahrscheinlich keine beliebte Antwort sein, aber Sie möchten dies mit ziemlicher Sicherheit nicht tun.Wenn Sie eine Kopie wünschen, bei der es sich um eine Zusammenführung handelt, verwenden Sie copy (oder Deepcopy, je nachdem, was Sie wollen) und dann aktualisieren.Die beiden Codezeilen sind viel besser lesbar – pythonischer – als die einzeilige Erstellung mit .items() + .items().Explizit ist besser als implizit.

Wenn Sie außerdem .items() (vor Python 3.0) verwenden, erstellen Sie eine neue Liste, die die Elemente aus dem Diktat enthält.Wenn Ihre Wörterbücher groß sind, ist das ein ziemlich großer Mehraufwand (zwei große Listen, die weggeworfen werden, sobald das zusammengeführte Diktat erstellt wird).update() kann effizienter arbeiten, da es das zweite Diktat Element für Element durchgehen kann.

Bezüglich Zeit:

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

Meiner Meinung nach lohnt sich die kleine Verlangsamung zwischen den ersten beiden für die Lesbarkeit.Darüber hinaus wurden Schlüsselwortargumente für die Wörterbucherstellung erst in Python 2.3 hinzugefügt, wohingegen copy() und update() in älteren Versionen funktionieren.

In einer Folgeantwort haben Sie nach der relativen Leistung dieser beiden Alternativen gefragt:

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

Zumindest auf meinem Rechner (einem ziemlich gewöhnlichen x86_64 mit Python 2.5.2) eine Alternative z2 ist nicht nur kürzer und einfacher, sondern auch deutlich schneller.Sie können dies selbst anhand des überprüfen timeit Modul, das mit Python geliefert wird.

Beispiel 1:identische Wörterbücher, die sich selbst 20 aufeinanderfolgende ganze Zahlen zuordnen:

% 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 gewinnt mit dem Faktor 3,5 oder so.Verschiedene Wörterbücher scheinen jedoch zu recht unterschiedlichen Ergebnissen zu führen z2 scheint immer die Nase vorn zu haben.(Wenn Sie inkonsistente Ergebnisse für erhalten Dasselbe Testen Sie, versuchen Sie es weiterzugeben -r mit einer Zahl größer als der Standardwert 3.)

Beispiel 2:Nicht überlappende Wörterbücher, die 252 kurze Zeichenfolgen ganzen Zahlen zuordnen und umgekehrt:

% 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 gewinnt etwa um den Faktor 10.Das ist meiner Meinung nach ein ziemlich großer Gewinn!

Nachdem ich diese beiden verglichen hatte, fragte ich mich, ob z1Die schlechte Leistung von 's könnte auf den Mehraufwand bei der Erstellung der beiden Elementlisten zurückzuführen sein, was mich wiederum zu der Frage veranlasste, ob diese Variante möglicherweise besser funktioniert:

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

Ein paar schnelle Tests, z.B.

% 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

lassen mich daraus schließen z3 ist etwas schneller als z1, aber bei weitem nicht so schnell wie z2.Es lohnt sich auf keinen Fall, so viel extra zu tippen.

In dieser Diskussion fehlt noch etwas Wichtiges, nämlich ein Leistungsvergleich dieser Alternativen mit der „offensichtlichen“ Möglichkeit, zwei Listen zusammenzuführen:Verwendung der update Methode.Um zu versuchen, die Dinge auf Augenhöhe mit den Ausdrücken zu halten, von denen keiner x oder y ändert, werde ich wie folgt eine Kopie von x erstellen, anstatt es direkt zu ändern:

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

Ein typisches Ergebnis:

% 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

Mit anderen Worten, z0 Und z2 scheinen im Wesentlichen die gleiche Leistung zu haben.Glauben Sie, dass das ein Zufall sein könnte?Ich tu nicht....

Tatsächlich würde ich sogar behaupten, dass es für reinen Python-Code unmöglich ist, eine bessere Leistung zu erbringen.Und wenn Sie in einem C-Erweiterungsmodul deutlich bessere Ergebnisse erzielen, kann ich mir vorstellen, dass die Python-Leute durchaus daran interessiert sein könnten, Ihren Code (oder eine Variation Ihres Ansatzes) in den Python-Kern zu integrieren.Python verwendet dict an vielen Orten;Die Optimierung seiner Abläufe ist eine große Sache.

Sie könnten dies auch schreiben als

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

wie Tony es tut, aber (nicht überraschend) hat der Unterschied in der Notation keinen messbaren Einfluss auf die Leistung.Verwenden Sie das, was Ihnen am besten gefällt.Natürlich hat er völlig Recht, wenn er darauf hinweist, dass die Version mit zwei Aussagen viel einfacher zu verstehen ist.

Ich wollte etwas Ähnliches, aber mit der Möglichkeit festzulegen, wie die Werte doppelter Schlüssel zusammengeführt werden, also habe ich das herausgehackt (aber nicht intensiv getestet).Offensichtlich handelt es sich hierbei nicht um einen einzelnen Ausdruck, sondern um einen einzelnen Funktionsaufruf.

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

In Python 3 können Sie verwenden Sammlungen.ChainMap Dadurch werden mehrere Diktate oder andere Zuordnungen gruppiert, um eine einzige, aktualisierbare Ansicht zu erstellen:

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

Aktualisieren Sie ein Diktat rekursiv/tiefgreifend

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

Demonstration:

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

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

print deepupdate(pluto_original, pluto_update)

Ausgänge:

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

Danke Rednaw für die Bearbeitung.

Die beste Version, die ich mir vorstellen könnte, ohne Copy zu verwenden, wäre:

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

Es ist schneller als dict(x.items() + y.items()) aber nicht so schnell wie n = copy(a); n.update(b), zumindest auf CPython.Diese Version funktioniert bei einer Änderung auch in Python 3 iteritems() Zu items(), was vom 2to3-Tool automatisch durchgeführt wird.

Persönlich gefällt mir diese Version am besten, weil sie in einer einzigen funktionalen Syntax ziemlich gut beschreibt, was ich will.Das einzige kleine Problem besteht darin, dass es nicht völlig offensichtlich ist, dass Werte von y Vorrang vor Werten von x haben, aber ich glaube nicht, dass es schwierig ist, das herauszufinden.

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

Bei Elementen mit Schlüsseln in beiden Wörterbüchern ('b') können Sie steuern, welches in der Ausgabe landet, indem Sie es ans Ende setzen.

Python 3.5 (PEP 448) ermöglicht eine schönere Syntaxoption:

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

Oder auch

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

Während die Frage bereits mehrmals beantwortet wurde, wurde diese einfache Lösung für das Problem noch nicht aufgeführt.

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

Es ist so schnell wie z0 und das oben erwähnte böse z2, aber leicht zu verstehen und zu ändern.

def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

Unter solch zwielichtigen und zweifelhaften Antworten ist dieses leuchtende Beispiel die einzig gute Möglichkeit, Diktate in Python zusammenzuführen, die vom Diktator auf Lebenszeit befürwortet wird Guido van Rossum sich selbst!Jemand anderes hat die Hälfte davon vorgeschlagen, es aber nicht in eine Funktion gepackt.

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

gibt:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

Wenn Sie denken, Lambdas seien böse, dann lesen Sie nicht weiter.Bei Bedarf können Sie die schnelle und speichereffiziente Lösung mit einem Ausdruck schreiben:

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}

Wie oben vorgeschlagen, ist die Verwendung von zwei Zeilen oder das Schreiben einer Funktion wahrscheinlich der bessere Weg.

Seien Sie pythonisch.Benutze einen Verständnis:

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

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

In Python3 ist die items Methode gibt keine Liste mehr zurück, sondern eher ein Sicht, die sich wie eine Menge verhält.In diesem Fall müssen Sie die Mengenvereinigung verwenden, da Sie mit verketten + wird nicht funktionieren:

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

Für Python3-ähnliches Verhalten in Version 2.7 ist die viewitems Methode sollte anstelle von funktionieren items:

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

Ich bevorzuge diese Notation ohnehin, da es natürlicher erscheint, sie als Mengenvereinigungsoperation und nicht als Verkettung zu betrachten (wie der Titel zeigt).

Bearbeiten:

Noch ein paar Punkte für Python 3.Beachten Sie zunächst, dass die dict(x, **y) Der Trick funktioniert in Python 3 nur, wenn die Tasten eingegeben werden y sind Strings.

Außerdem Raymond Hettingers Chainmap Antwort ist ziemlich elegant, da es eine beliebige Anzahl von Diktaten als Argumente annehmen kann, aber aus den Dokumenten Es sieht so aus, als ob es nacheinander eine Liste aller Diktate für jede Suche durchsucht:

Lookups durchsuchen nacheinander die zugrunde liegenden Zuordnungen, bis ein Schlüssel gefunden wird.

Dies kann zu Verzögerungen führen, wenn Ihre Anwendung viele Suchvorgänge enthält:

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

Also etwa eine Größenordnung langsamer für Suchvorgänge.Ich bin ein Fan von Chainmap, aber es sieht weniger praktisch aus, wenn es viele Suchvorgänge gibt.

Missbrauch führt zu einer Ein-Ausdruck-Lösung für Matthews Antwort:

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

Sie sagten, Sie wollten einen Ausdruck, also habe ich ihn beschimpft lambda um einen Namen zu binden, und Tupel, um Lambdas Ein-Ausdruck-Limit zu überschreiben.Fühlen Sie sich frei, zusammenzuzucken.

Sie können dies natürlich auch tun, wenn Sie es nicht kopieren möchten:

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

Einfache Lösung mit itertools, die die Ordnung bewahrt (letztere Diktate haben Vorrang)

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

Und es ist die Verwendung:

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

Zwei Wörterbücher

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

N Wörterbücher

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

sum hat schlechte Leistung.Sehen https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

Auch wenn die Antworten dafür gut waren seicht Wörterbuch führt keine der hier definierten Methoden tatsächlich eine tiefe Wörterbuchzusammenführung durch.

Es folgen Beispiele:

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

Man würde ein Ergebnis wie dieses erwarten:

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

Stattdessen erhalten wir Folgendes:

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

Der Eintrag „eins“ hätte „tiefe_2“ und „extra“ als Elemente in seinem Wörterbuch enthalten müssen, wenn es sich wirklich um eine Zusammenführung handelte.

Auch die Verwendung von Chain funktioniert nicht:

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

Ergebnisse in:

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

Die tiefe Verschmelzung, die RCwesick durchgeführt hat, führt ebenfalls zum gleichen Ergebnis.

Ja, es funktioniert, die Beispielwörterbücher zusammenzuführen, aber keines davon ist ein allgemeiner Mechanismus zum Zusammenführen.Ich werde dies später aktualisieren, sobald ich eine Methode schreibe, die eine echte Zusammenführung durchführt.

Basierend auf Ideen hier und anderswo habe ich eine Funktion verstanden:

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

Verwendung (getestet in 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})

Sie könnten stattdessen ein Lambda verwenden.

Das Problem, das ich mit den bisher aufgeführten Lösungen habe, besteht darin, dass im zusammengeführten Wörterbuch der Wert für Schlüssel „b“ 10 ist, meiner Meinung nach jedoch 12 sein sollte.Vor diesem Hintergrund präsentiere ich Folgendes:

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

Ergebnisse:

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

Dies kann mit einem einzigen Diktatverständnis erfolgen:

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

Meiner Meinung nach die beste Antwort für den Teil „Einzelausdruck“, da keine zusätzlichen Funktionen erforderlich sind und er kurz ist.

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

Dies sollte Ihr Problem lösen.

(Nur für Python2.7*;es gibt einfachere Lösungen für Python3*.)

Wenn Sie dem Import eines Standardbibliotheksmoduls nicht abgeneigt sind, können Sie dies tun

from functools import reduce

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

(Der or a bisschen in der lambda ist notwendig, weil dict.update kommt immer wieder zurück None auf Erfolg.)

Das ist so albern .update gibt nichts zurück.
Ich verwende einfach eine einfache Hilfsfunktion, um das Problem zu lösen:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

Beispiele:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

Mit einem Diktatverständnis können Sie das tun

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}

dc = {xi:(x[xi] if xi not in list(y.keys()) 
           else y[xi]) for xi in list(x.keys())+(list(y.keys()))}

gibt

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

Beachten Sie die Syntax für if else im Verständnis

{ (some_key if condition else default_key):(something_if_true if condition 
          else something_if_false) for key, value in dict_.items() }

Ich weiß, dass dies nicht wirklich zu den Besonderheiten der Fragen passt („Einzeiler“), aber seitdem keiner Da die oben genannten Antworten in diese Richtung gingen, während viele, viele Antworten sich mit dem Leistungsproblem befassten, hatte ich das Gefühl, dass ich meine Gedanken einbringen sollte.

Abhängig vom Anwendungsfall ist es möglicherweise nicht erforderlich, ein „echtes“ zusammengeführtes Wörterbuch der angegebenen Eingabewörterbücher zu erstellen.A Sicht was dies tut, könnte in vielen Fällen ausreichend sein, d.e.ein Objekt, das handelt wie Das zusammengeführte Wörterbuch würde dies tun, ohne es vollständig zu berechnen.Sozusagen eine Lazy-Version des zusammengeführten Wörterbuchs.

In Python ist dies ziemlich einfach und kann mit dem am Ende meines Beitrags gezeigten Code durchgeführt werden.Vor diesem Hintergrund wäre die Antwort auf die ursprüngliche Frage:

z = MergeDict(x, y)

Wenn Sie dieses neue Objekt verwenden, verhält es sich wie ein zusammengeführtes Wörterbuch, hat jedoch eine konstante Erstellungszeit und einen konstanten Speicherbedarf, während die ursprünglichen Wörterbücher unberührt bleiben.Die Herstellung ist viel kostengünstiger als bei den anderen vorgeschlagenen Lösungen.

Wenn Sie das Ergebnis häufig verwenden, werden Sie natürlich irgendwann an die Grenze stoßen, bei der die Erstellung eines echten zusammengeführten Wörterbuchs die schnellere Lösung gewesen wäre.Wie gesagt, es hängt von Ihrem Anwendungsfall ab.

Wenn Sie jemals das Gefühl hatten, dass Sie eine echte Fusion bevorzugen würden dict, dann anrufen dict(z) würde es produzieren (aber natürlich viel teurer als die anderen Lösungen, also ist das einfach erwähnenswert).

Sie können diese Klasse auch verwenden, um eine Art Copy-on-Write-Wörterbuch zu erstellen:

a = { 'x': 3, 'y': 4 }
b = MergeDict(a)  # we merge just one dict
b['x'] = 5
print b  # will print {'x': 5, 'y': 4}
print a  # will print {'y': 4, 'x': 3}

Hier ist der einfache Code von MergeDict:

class MergeDict(object):
  def __init__(self, *originals):
    self.originals = ({},) + originals[::-1]  # reversed

  def __getitem__(self, key):
    for original in self.originals:
      try:
        return original[key]
      except KeyError:
        pass
    raise KeyError(key)

  def __setitem__(self, key, value):
    self.originals[0][key] = value

  def __iter__(self):
    return iter(self.keys())

  def __repr__(self):
    return '%s(%s)' % (
      self.__class__.__name__,
      ', '.join(repr(original)
          for original in reversed(self.originals)))

  def __str__(self):
    return '{%s}' % ', '.join(
        '%r: %r' % i for i in self.iteritems())

  def iteritems(self):
    found = set()
    for original in self.originals:
      for k, v in original.iteritems():
        if k not in found:
          yield k, v
          found.add(k)

  def items(self):
    return list(self.iteritems())

  def keys(self):
    return list(k for k, _ in self.iteritems())

  def values(self):
    return list(v for _, v in self.iteritems())
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top