Wie füge ich zwei Wörterbücher in einem einzigen Ausdruck in Python zusammen?

Lesezeit: 8 Minuten

Benutzeravatar von Carl Meyer
Karl Meier

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

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}
z = merge(x, y)

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

Immer wenn ein Schlüssel k In beiden Wörterbüchern ist nur der Wert vorhanden y[k] sollte zurückgehalten werden.

Benutzeravatar von Thomas Vander Stichele
Thomas Vander Stichele

In Ihrem Fall können Sie Folgendes tun:

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

Dies wird, wie Sie es wünschen, das letzte Diktat einfügen zund machen Sie den Wert für key b ordnungsgemäß von der Sekunde überschrieben werden (y) 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 entfernen list() Anrufe. So erstellen Sie z:

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

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

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

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

    – Gringo Suave

    11. März 2020 um 17:22 Uhr


  • Um zu verdeutlichen, warum dies die Kriterien der Frage nicht erfüllt: Es ist kein einzelner Ausdruck und es gibt kein z zurück.

    – Alexander Ach

    21. März 2013 um 13:15 Uhr


  • Sagen Sie es so: Wenn Sie zwei Kommentarzeilen schreiben müssen, die Ihre eine Codezeile den Leuten erklären, denen Sie Ihren Code übergeben, haben Sie es wirklich in einer Zeile gemacht? 🙂 Ich stimme voll und ganz zu, dass Python dafür nicht gut ist: Es sollte einen viel einfacheren Weg geben. Diese Antwort ist zwar eher pythonisch, aber ist sie wirklich so explizit oder klar? Update ist keine der “Kern”-Funktionen, die Menschen häufig verwenden.

    – erich

    19. Oktober 2017 um 13:07 Uhr


  • Nun, wenn die Leute darauf bestehen, es zu einem Einzeiler zu machen, können Sie das immer tun (lambda z: z.update(y) or z)(x.copy()) 😛

    – Turm

    24. Februar 2020 um 12:43 Uhr

  • @WilliamMartens, es war kein Witz. Aber seien wir ehrlich, wenn Sie für einzeilige Ausdrücke optimieren, optimieren Sie für das Falsche.

    – Alexander Ach

    15. Juli 2022 um 9:15 Uhr

  • @AlexanderOh ja; Ich stimme zu! habe es toll!

    – Wilhelm Martens

    16. Juli 2022 um 5:45 Uhr

Benutzeravatar von Carl Meyer
Karl Meier

Eine andere, prägnantere Option:

z = dict(x, **y)

Notiz: Dies ist eine beliebte Antwort geworden, aber es ist wichtig darauf hinzuweisen, dass if y keine Zeichenfolgenschlü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. Daher kann ich diese Technik nicht für aufwärtskompatiblen oder implementierungsübergreifenden portablen Code empfehlen, was wirklich bedeutet, dass sie vollständig vermieden werden sollte.

  • Funktioniert gut in Python 3 und PyPy und PyPy 3, kann nicht mit Jython oder Iron sprechen. Angesichts dieses Musters ist ausdrücklich dokumentiert (siehe das dritte Konstruktorformular in dieser Dokumentation) Ich würde argumentieren, dass es sich nicht um ein “Implementierungsdetail” handelt, sondern um eine absichtliche Verwendung von Funktionen.

    – amcgregor

    12. April 2019 um 13:10 Uhr

  • @amcgregor Sie haben den Schlüsselsatz “wenn y irgendwelche Nicht-String-Schlüssel hat” verpasst. Das funktioniert in Python3 nicht; Die Tatsache, dass es in CPython 2 funktioniert, ist ein Implementierungsdetail, auf das man sich nicht verlassen kann. IFF alle Ihre Schlüssel sind garantiert Zeichenfolgen, dies ist eine vollständig unterstützte Option.

    – Karl Meier

    10. Mai 2019 um 16:27 Uhr

Benutzeravatar von twasbrillig
twasbrillig

Dies wird wahrscheinlich keine beliebte Antwort sein, aber Sie möchten dies mit ziemlicher Sicherheit nicht tun. Wenn Sie eine zusammengeführte Kopie wünschen, verwenden Sie copy (bzw tiefe Kopie, je nachdem, was Sie wollen) und dann aktualisieren. Die zwei Codezeilen sind viel besser lesbar – mehr pythonisch – als die einzeilige Erstellung mit .items() + .items(). Explizit ist besser als implizit.

Wenn Sie .items() (vor Python 3.0) verwenden, erstellen Sie außerdem eine neue Liste, die die Elemente aus dem Diktat enthält. Wenn Ihre Wörterbücher groß sind, dann ist das ziemlich viel Overhead (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 durchlaufen 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 winzige Verlangsamung zwischen den ersten beiden für die Lesbarkeit. Außerdem wurden Schlüsselwortargumente für die Wörterbucherstellung erst in Python 2.3 hinzugefügt, wohingegen copy() und update() in älteren Versionen funktionieren.

  • Funktioniert gut in Python 3 und PyPy und PyPy 3, kann nicht mit Jython oder Iron sprechen. Angesichts dieses Musters ist ausdrücklich dokumentiert (siehe das dritte Konstruktorformular in dieser Dokumentation) Ich würde argumentieren, dass es sich nicht um ein “Implementierungsdetail” handelt, sondern um eine absichtliche Verwendung von Funktionen.

    – amcgregor

    12. April 2019 um 13:10 Uhr

  • @amcgregor Sie haben den Schlüsselsatz “wenn y irgendwelche Nicht-String-Schlüssel hat” verpasst. Das funktioniert in Python3 nicht; Die Tatsache, dass es in CPython 2 funktioniert, ist ein Implementierungsdetail, auf das man sich nicht verlassen kann. IFF alle Ihre Schlüssel sind garantiert Zeichenfolgen, dies ist eine vollständig unterstützte Option.

    – Karl Meier

    10. Mai 2019 um 16:27 Uhr

Benutzeravatar des Blechmanns
der Blechmann

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 (ein ziemlich gewöhnlicher x86_64 mit Python 2.5.2) alternativ z2 ist nicht nur kürzer und einfacher, sondern auch deutlich schneller. Sie können dies anhand der selbst überprüfen 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 um den Faktor 3,5 oder so. Verschiedene Wörterbücher scheinen jedoch zu recht unterschiedlichen Ergebnissen zu führen z2 scheint immer voraus zu sein. (Wenn Sie inkonsistente Ergebnisse für die Dasselbe testen, versuchen, vorbeizukommen -r mit einer Zahl größer als die Vorgabe 3.)

Beispiel 2: Nicht überlappende Wörterbücher, die 252 kurze Zeichenfolgen auf Ganzzahlen 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 z1Die schlechte Leistung von könnte auf den Aufwand für die Erstellung der beiden Elementlisten zurückgeführt werden, was mich wiederum zu der Frage veranlasste, ob diese Variante besser funktionieren könnte:

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

Ein paar schnelle Tests, z

% 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

lässt mich darauf schließen z3 ist etwas schneller als z1aber nicht annähernd so schnell wie z2. Es lohnt sich definitiv nicht, die ganze zusätzliche Eingabe zu machen.

Dieser Diskussion fehlt noch etwas Wichtiges, nämlich ein Leistungsvergleich dieser Alternativen mit der “offensichtlichen” Art der Zusammenführung zweier Listen: die Verwendung von update Methode. Um zu versuchen, die Dinge mit den Ausdrücken gleichzusetzen, von denen keiner x oder y ändert, werde ich eine Kopie von x erstellen, anstatt es direkt zu ä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 Und z2 scheinen im Wesentlichen die gleiche Leistung zu haben. Glauben Sie, dass dies ein Zufall sein könnte? Ich tu nicht….

Ich würde sogar so weit gehen zu behaupten, dass es für reinen Python-Code unmöglich ist, etwas Besseres zu leisten. Und wenn Sie in einem C-Erweiterungsmodul deutlich besser abschneiden, könnten die Python-Leute daran interessiert sein, Ihren Code (oder eine Variation Ihres Ansatzes) in den Python-Kern zu integrieren. Python verwendet dict an vielen Orten; Die Optimierung des Betriebs ist eine große Sache.

Das könnte man auch so schreiben

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

wie Tony es tut, aber (nicht überraschend) stellt sich heraus, dass der Unterschied in der Notation keinen messbaren Einfluss auf die Leistung hat. Verwenden Sie, was für Sie richtig aussieht. Natürlich hat er absolut Recht, wenn er darauf hinweist, dass die Version mit zwei Aussagen viel einfacher zu verstehen ist.

  • Dies funktioniert nicht in Python 3; items() ist nicht catenable, und iteritems ist nicht vorhanden.

    – Antti Haapala – Слава Україні

    16. März 2015 um 5:50 Uhr

1444070cookie-checkWie füge ich zwei Wörterbücher in einem einzigen Ausdruck in Python zusammen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy