6830 Stimmen

Wie führe ich zwei Wörterbücher in einem einzigen Ausdruck zusammen (Vereinigung von Wörterbüchern)?

Ich möchte zwei Wörterbücher zu einem neuen Wörterbuch zusammenführen.

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

z = merge(x, y)

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

使用方法 x.update(y) modifiziert x anstelle eines neuen Wörterbuchs zu erstellen. Ich möchte auch, dass die Konfliktbehandlung des letzten Gewinners von dict.update() auch.

1809voto

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

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

Dadurch wird das endgültige Diktat wie gewünscht in z und machen den Wert für den Schlüssel b ordnungsgemäß durch die zweite überschrieben werden ( y ) den Wert von dict:

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

Wenn Sie Python 2 verwenden, können Sie sogar die list() Anrufe. Zum Erstellen von z:

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

Wenn Sie Python Version 3.9.0a4 oder höher verwenden, dann können Sie direkt verwenden:

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

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

19 Stimmen

Verwenden Sie dies nicht, da es sehr ineffizient ist. (Siehe die timeit Ergebnisse unten.) Es kann in den Py2 Tagen notwendig gewesen sein, wenn eine Wrapper-Funktion nicht eine Option war, aber diese Tage sind jetzt vorbei.

750voto

Matthew Schinckel Punkte 33617

Eine Alternative:

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

106 Stimmen

Um zu verdeutlichen, warum dies nicht den Kriterien der Frage entspricht: Es ist kein einzelner Ausdruck und gibt nicht z zurück.

22 Stimmen

Sagen wir es mal so: Wenn Sie zwei Zeilen Kommentare schreiben müssen, um den Leuten, denen Sie Ihren Code übergeben, Ihre eine Zeile Code zu erklären... haben Sie es wirklich in einer Zeile geschafft? :) Ich stimme voll und ganz zu, dass Python dafür nicht geeignet ist: es sollte einen viel einfacheren Weg geben. Diese Antwort ist zwar eher pythonisch, aber ist sie wirklich so eindeutig oder klar? Update gehört nicht zu den "Kernfunktionen", die häufig verwendet werden.

15 Stimmen

Nun, wenn die Leute darauf bestehen, es zu einem Einzeiler zu machen, kann man das immer tun (lambda z: z.update(y) or z)(x.copy()) :P

441voto

Carl Meyer Punkte 113574

Eine andere, prägnantere Option:

z = dict(x, **y)

Note : Dies ist eine beliebte Antwort, aber es ist wichtig, darauf hinzuweisen, dass, wenn y keine Nicht-String-Schlüssel hat, 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 . Ich kann diese Technik also nicht für vorwärtskompatiblen oder implementierungsübergreifend portablen Code empfehlen, was eigentlich bedeutet, dass sie ganz vermieden werden sollte.

3 Stimmen

Funktioniert gut in Python 3 und PyPy und PyPy 3 kann nicht mit Jython oder Iron sprechen. Da dieses Muster ist ausdrücklich dokumentiert (siehe das dritte Konstruktorformular in dieser Dokumentation) Ich würde behaupten, dass es sich dabei nicht um ein "Implementierungsdetail" handelt, sondern um die absichtliche Verwendung einer Funktion.

19 Stimmen

@amcgregor Du hast den Schlüsselsatz "if y has any non-string keys" überlesen. Das ist es, was in Python3 nicht funktioniert; die Tatsache, dass es in CPython 2 funktioniert, ist ein Implementierungsdetail, auf das man sich nicht verlassen kann. Wenn alle Ihre Schlüssel garantiert Strings sind, ist dies eine vollständig unterstützte Option.

257voto

Tony Meyer Punkte 9701

Dies ist wahrscheinlich keine beliebte Antwort, aber Sie wollen dies mit Sicherheit nicht tun. Wenn Sie eine Kopie wollen, die eine Zusammenführung ist, dann verwenden Sie copy (oder deepcopy (je nachdem, was Sie wollen) und dann aktualisieren. Die zwei Codezeilen sind viel lesbarer - 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, dann ist das ein ziemlicher Overhead (zwei große Listen, die weggeworfen werden, sobald das zusammengeführte Diktat erstellt ist). update() kann effizienter arbeiten, weil es das zweite Diktat Element für Element durchlaufen kann.

In Bezug auf 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

IMO ist die kleine Verlangsamung zwischen den ersten beiden für die Lesbarkeit lohnenswert. Darüber hinaus wurden Schlüsselwortargumente für die Erstellung von Wörterbüchern erst in Python 2.3 hinzugefügt, während copy() und update() auch in älteren Versionen funktionieren.

195voto

zaphod Punkte 4366

In einer weiteren Antwort fragten Sie nach der relativen Leistung dieser beiden Alternativen:

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

Zumindest auf meinem Rechner (ein ganz normaler x86_64 mit Python 2.5.2) werden alternative z2 ist nicht nur kürzer und einfacher, sondern auch wesentlich schneller. Sie können dies selbst überprüfen, indem Sie die timeit Modul, das mit Python geliefert wird.

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

% 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 einem Faktor von etwa 3,5. Verschiedene Wörterbücher scheinen recht unterschiedliche Ergebnisse zu liefern, aber z2 scheint immer die Nase vorn zu haben. (Wenn Sie inkonsistente Ergebnisse für die 同じ Test, versuchen Sie die Eingabe von -r mit einer größeren Zahl als der Standardzahl 3).

Beispiel 2: Nicht überlappende Wörterbücher, die 252 kurze Zeichenketten auf ganze Zahlen abbilden 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 z1 Das schlechte Abschneiden der beiden Listen könnte auf den Aufwand für die Erstellung der beiden Listen zurückzuführen sein, was mich wiederum zu der Frage veranlasste, ob diese Variante nicht besser funktionieren könnte:

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

Einige 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 zu dem Schluss kommen, dass z3 ist etwas schneller als z1 , aber nicht annähernd so schnell wie z2 . Es lohnt sich definitiv nicht, all die zusätzliche Tipparbeit zu leisten.

In dieser Diskussion fehlt noch etwas Wichtiges, nämlich ein Leistungsvergleich dieser Alternativen mit dem "offensichtlichen" Weg, zwei Listen zusammenzuführen: die Verwendung des update Methode. Um zu versuchen, die Dinge gleichberechtigt mit den Ausdrücken zu halten, von denen keiner x oder y verändert, werde ich eine Kopie von x erstellen, anstatt es an Ort und Stelle zu verändern, wie folgt:

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 y z2 scheinen im Wesentlichen die gleiche Leistung zu haben. Glauben Sie, dass dies ein Zufall sein könnte? Ich weiß es nicht....

Ich würde sogar so weit gehen zu behaupten, dass es für reinen Python-Code unmöglich ist, etwas Besseres zu tun als dies. Und wenn Sie es in einem C-Erweiterungsmodul deutlich besser machen können, könnte ich mir vorstellen, dass die Python-Leute durchaus daran interessiert sein könnten, Ihren Code (oder eine Variation Ihres Ansatzes) in den Python-Kern einzubauen. Python verwendet dict an vielen Orten; die Optimierung ihrer Abläufe ist eine große Sache.

Man könnte dies auch so schreiben

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

wie Tony, aber (nicht überraschend) hat der Unterschied in der Notation keine messbaren Auswirkungen auf die Leistung. Verwenden Sie das, was Ihnen am besten erscheint. Natürlich hat er völlig Recht, wenn er darauf hinweist, dass die Version mit zwei Sätzen viel einfacher zu verstehen ist.

9 Stimmen

Dies funktioniert nicht in Python 3; items() nicht verkettbar ist, und iteritems gibt es nicht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X