Wie kann ich zwei Python-Wörterbücher in einem einzigen Ausdruck zusammenführen?
Für Wörterbücher x
und y
, z
wird ein seicht aus Wörterbuch mit Werten fusionierte y
von denen ersetzen 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)
In Python 3.9.0a4 oder höher (endgültiger Veröffentlichungstermin ca. Oktober 2020): PEP-584 , die hier beschrieben wurde diese weiter zu vereinfachen umgesetzt:
z = x | y # NOTE: 3.9+ ONLY
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 ist, ein neues Wörterbuch ( z
) zu erhalten, in dem die Werte zusammengeführt werden und die Werte des zweiten Dikts die Werte des ersten überschreiben.
>>> z
{'a': 1, 'b': 3, 'c': 4}
Eine neue Syntax hierfür, die in PEP 448 vorgeschlagen und ab Python 3.5 verfügbar ist, ist
z = {**x, **y}
Und es ist in der Tat 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 im Release-Zeitplan für 3.5, PEP 478 , implementiert angezeigt und hat nun den Weg in das Dokument " Was ist neu in Python 3.5" gefunden .
Da sich jedoch viele Organisationen noch in Python 2 befinden, möchten Sie dies möglicherweise abwärtskompatibel tun. Die klassische pythonische Methode, die in Python 2 und Python 3.0-3.4 verfügbar ist, besteht darin, dies in zwei Schritten zu tun:
z = x.copy()
z.update(y) # which returns None since it mutates z
In beiden Ansätzen y
wird es an zweiter Stelle stehen und seine Werte werden die Werte ersetzen x
und somit 'b'
auf 3
unser Endergebnis verweisen .
Noch nicht in Python 3.5, möchte aber einen einzelnen Ausdruck
Wenn Sie noch nicht mit Python 3.5 arbeiten oder abwärtskompatiblen Code schreiben müssen und dies in einem einzelnen Ausdruck möchten , ist es am leistungsfähigsten, wenn Sie ihn in eine Funktion einfü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 hast du einen einzigen Ausdruck:
z = merge_two_dicts(x, y)
Sie können auch eine Funktion zum Zusammenführen einer undefinierten Anzahl von Diktaten von Null bis zu einer sehr großen Anzahl erstellen:
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. zB gegebene Diktate a
an g
:
z = merge_dicts(a, b, c, d, e, f, g)
und Schlüsselwertpaare in g
haben Vorrang vor Diktaten a
an f
und so weiter.
Kritik anderer Antworten
Verwenden Sie nicht das, was Sie in der zuvor akzeptierten Antwort sehen:
z = dict(x.items() + y.items())
In Python 2 erstellen Sie zwei Listen im Speicher für jedes Diktat, erstellen eine dritte Liste im Speicher mit einer Länge, die der Länge der ersten beiden zusammen entspricht, und verwerfen dann alle drei Listen, um das Diktat zu erstellen. In Python 3 schlägt dies fehl, da Sie zwei dict_items
Objekte und nicht zwei Listen hinzufügen.
>>> 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.
In ähnlicher Weise schlägt die Vereinigung von items()
in Python 3 ( viewitems()
in Python 2.7) fehl, wenn Werte nicht zerlegbare Objekte sind (wie z. B. Listen). Selbst wenn Ihre Werte hashbar sind, ist das Verhalten in Bezug auf die Priorität undefiniert , da Mengen semantisch ungeordnet sind. Also mach das nicht:
>>> c = dict(a.items() | b.items())
Dieses Beispiel zeigt, was passiert, wenn Werte nicht verwertbar sind:
>>> 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, in 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 verwendet den dict
Konstruktor und ist sehr schnell und speichereffizient (sogar etwas mehr als unser zweistufiger Prozess), es sei denn, Sie wissen genau, was hier passiert (dh das zweite Diktat wird als Schlüsselwortargument an das Diktat übergeben Konstruktor), es ist schwer zu lesen, es ist nicht die beabsichtigte Verwendung, und so ist es nicht Pythonic.
Hier ist ein Beispiel für die Verwendung in Django .
Dicts sollen Hash-Schlüssel (z. B. Frozensets oder Tupel) verwenden. Diese Methode schlägt jedoch 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
Aus der Mailingliste schrieb Guido van Rossum, der Schöpfer der Sprache:
Ich kann das Diktat ({}, ** {1: 3}) für illegal erklären, da es sich schließlich um einen Missbrauch des ** Mechanismus handelt.
und
Anscheinend wird dict (x, ** y) als "cooler Hack" für "x.update (y) aufrufen und x zurückgeben" verwendet. Persönlich finde ich es eher verabscheuungswürdig als cool.
Es ist mein Verständnis (sowie das Verständnis des Schöpfers der Sprache ), dass die beabsichtigte Verwendung darin dict(**y)
besteht, Diktate zu Lesbarkeitszwecken zu erstellen, z.
dict(a=1, b=10, c=11)
anstatt
{'a': 1, 'b': 10, 'c': 11}
Antwort auf Kommentare
Trotz allem, was Guido sagt, entspricht dict(x, **y)
es der Diktatspezifikation, die übrigens. 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 kein kurzes Diktieren. Die Verwendung des Operators ** an dieser Stelle stellt auch keinen Missbrauch des Mechanismus dar. Tatsächlich wurde ** genau darauf ausgelegt, 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 normale Diktate annehmen, während Benutzer nur Schlüsselwortargumente übergeben müssen, die Zeichenfolgen sind. Alle anderen Callables haben es erzwungen. 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 angesichts anderer Implementierungen von Python (Pypy, Jython, IronPython) schlecht. Daher wurde es in Python 3 behoben, da diese Verwendung eine bahnbrechende Änderung sein könnte.
Ich sage Ihnen, 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. Die Lesbarkeit zählt.
Meine Antwort: merge_two_dicts(x, y)
scheint mir tatsächlich viel klarer zu sein, wenn wir uns tatsächlich Gedanken über die Lesbarkeit machen. Und es ist nicht vorwärtskompatibel, da Python 2 zunehmend veraltet ist.
{**x, **y}
scheint nicht mit verschachtelten Wörterbüchern umzugehen. Der Inhalt verschachtelter Schlüssel wird einfach überschrieben und nicht zusammengeführt. [...] Ich wurde von diesen Antworten verbrannt, die nicht rekursiv zusammengeführt werden, und ich war überrascht, dass niemand dies erwähnte. In meiner Interpretation des Wortes "Zusammenführen" beschreiben diese Antworten "Aktualisieren eines Diktats mit einem anderen" und nicht Zusammenführen.
Ja. Ich muss Sie auf die Frage zurückführen, die eine flache Zusammenführung von zwei Wörterbüchern erfordert , wobei die Werte des ersten durch die Werte des zweiten überschrieben werden - in einem einzigen Ausdruck.
Wenn Sie zwei Wörterbücher mit Wörterbüchern annehmen, werden diese möglicherweise rekursiv in einer einzigen Funktion zusammengeführt. Sie sollten jedoch darauf achten, die Wörterbücher aus beiden Quellen nicht 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 normalerweise 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
Verwendungszweck:
>>> 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, geht weit über den Rahmen dieser Frage hinaus, daher werde ich Sie auf meine Antwort auf die kanonische Frage zu einem "Zusammenführen von Wörterbüchern der Wörterbücher" hinweisen .
Weniger leistungsfähig, aber korrekte Ad-hocs
Diese Ansätze sind weniger performant, liefern jedoch ein korrektes Verhalten. Sie werden viel weniger performant als copy
und update
oder das neue auspacken , weil sie durchlaufen jedes Schlüssel-Wert - Paar auf einer höheren Abstraktionsebene, aber sie tun respektieren die Rangordnung (letztere dicts 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 in 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()))
Performance-Analyse
Ich werde nur die Leistungsanalyse der Verwendungen durchführen, von denen bekannt ist, dass sie sich korrekt verhalten.
import timeit
Das Folgende wird unter Ubuntu 14.04 gemacht
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
z = x | y