Wie füge ich Wörterbücher in Python zusammen?

89
d3 = dict(d1, **d2)

Ich verstehe, dass dies das Wörterbuch zusammenführt. Aber ist es einzigartig? Was ist, wenn d1 den gleichen Schlüssel wie d2 hat, aber einen anderen Wert? Ich möchte, dass d1 und d2 zusammengeführt werden, aber d1 hat Priorität, wenn es einen doppelten Schlüssel gibt.

TIMEX
quelle
9
Bitte beachten Sie, dass dieser Trick als Missbrauch der **Übergabe von Schlüsselwortargumenten angesehen wird, es sei denn, alle Schlüssel von d2sind Zeichenfolgen. Wenn nicht alle Schlüssel von d2Zeichenfolgen sind, schlägt dies in Python 3.2 und in alternativen Implementierungen von Python wie Jython, IronPython und PyPy fehl. Siehe beispielsweise mail.python.org/pipermail/python-dev/2010-April/099459.html .
Mark Dickinson

Antworten:

151

Sie können die .update()Methode verwenden, wenn Sie das Original d2nicht mehr benötigen :

Aktualisieren Sie das Wörterbuch mit den Schlüssel / Wert-Paaren anderer, wobei vorhandene Schlüssel überschrieben werden . Zurück None.

Z.B:

>>> d1 = {'a': 1, 'b': 2} 
>>> d2 = {'b': 1, 'c': 3}
>>> d2.update(d1)
>>> d2
{'a': 1, 'c': 3, 'b': 2}

Aktualisieren:

Natürlich können Sie das Wörterbuch zuerst kopieren, um ein neues zusammengeführtes zu erstellen. Dies kann erforderlich sein oder auch nicht. Falls Ihr Wörterbuch zusammengesetzte Objekte (Objekte, die andere Objekte wie Listen oder Klasseninstanzen enthalten) enthält, copy.deepcopysollte dies ebenfalls berücksichtigt werden.

Felix Kling
quelle
1
In diesem Fall sollten d1-Elemente korrekt Priorität erhalten, wenn widersprüchliche Schlüssel gefunden werden
Trey Hunner
Falls Sie es noch brauchen, machen Sie einfach eine Kopie. d3 = d2.copy () d3.update (d1), aber ich würde gerne sehen, dass d1 + d2 zur Sprache hinzugefügt wird.
Stach
4
d1 + d2 ist problematisch, weil ein Wörterbuch bei Konflikten Vorrang haben muss und es nicht besonders offensichtlich ist, welches.
rjh
d1 + d2 wird immer nur implementiert, wenn Python eine Multimap erhält, andernfalls ist die Mehrdeutigkeit für den Benutzer für den 8-Byte-Tippgewinn zu verwirrend.
Nick Bastin
Sie haben Objekte im Wörterbuch in diesem Beispiel: isinstance(int, object) is Truenoch deepcopynicht notwendig erscheint.
Antony Hatchkins
43

In Python2

d1={'a':1,'b':2}
d2={'a':10,'c':3}

d1 überschreibt d2:

dict(d2,**d1)
# {'a': 1, 'c': 3, 'b': 2}

d2 überschreibt d1:

dict(d1,**d2)
# {'a': 10, 'c': 3, 'b': 2}

Dieses Verhalten ist nicht nur ein Zufall bei der Implementierung. es ist in der Dokumentation garantiert :

Wenn ein Schlüssel sowohl im Positionsargument als auch als Schlüsselwortargument angegeben wird, bleibt der dem Schlüsselwort zugeordnete Wert im Wörterbuch erhalten.

unutbu
quelle
3
Ihre Beispiele schlagen in Python 3.2 und in aktuellen Versionen von Jython, PyPy und IronPython fehl (was zu einem TypeError führt): Bei diesen Versionen von Python sollten beim Übergeben eines Diktats mit der **Notation alle Schlüssel dieses Diktats Zeichenfolgen sein. Weitere Informationen finden Sie im Python-Dev-Thread ab mail.python.org/pipermail/python-dev/2010-April/099427.html .
Mark Dickinson
@ Mark: Danke für das Heads Up. Ich habe den Code so bearbeitet, dass er mit Nicht-CPython-Implementierungen kompatibel ist.
Unutbu
3
Es schlägt fehl, wenn Ihre Schlüssel Tupel von Zeichenfolgen und Zahlen sind. für zB. d1 = {(1, 'a'): 1, (1, 'b'): 0,} d2 = {(1, 'a'): 1, (2, 'b'): 2, (2, 'a'): 1,}
MySchizoBuddy
Informationen zur Entpackungssyntax finden Sie in diesem Beitrag zu Änderungen in Python 3.5.
Ioannis Filippidis
Ich wollte sagen, dass das d = dict(**d1, **d2)funktioniert, aber darauf verweist @IoannisFilippidis in ihrem Kommentar. Vielleicht wäre es klarer gewesen, das Snippet hier aufzunehmen, also hier ist es.
Dwanderson
14

Wenn Sie d1in den Konflikten Vorrang haben möchten , gehen Sie wie folgt vor:

d3 = d2.copy()
d3.update(d1)

Ansonsten umgekehrt d2und d1.

tzot
quelle
1

Meine Lösung besteht darin, eine Zusammenführungsfunktion zu definieren . Es ist nicht raffiniert und kostet nur eine Zeile. Hier ist der Code in Python 3.

from functools import reduce
from operator import or_

def merge(*dicts):
    return { k: reduce(lambda d, x: x.get(k, d), dicts, None) for k in reduce(or_, map(lambda x: x.keys(), dicts), set()) }

Tests

>>> d = {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> d_letters = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d, d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d_letters, d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge(d)
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
>>> merge(d_letters)
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z'}
>>> merge()
{}

Es funktioniert für eine beliebige Anzahl von Wörterbuchargumenten. Gab es doppelte Schlüssel in diesem Wörterbuch, gewinnt der Schlüssel aus dem Wörterbuch ganz rechts in der Argumentliste.

Lei Zhao
quelle
1
Eine einfache Schleife mit einem .updateAufruf ( merged={}gefolgt von for d in dict: merged.update(d)) wäre kürzer, lesbarer und effizienter.
Mark Dickinson
1
Oder wenn Sie wirklich reduceund lambdas verwenden möchten , wie wäre es return reduce(lambda x, y: x.update(y) or x, dicts, {})?
Mark Dickinson
1
Sie können Ihren Code in der Shell ausprobieren und prüfen, ob er korrekt ist. Ich habe versucht, eine Funktion zu schreiben, die verschiedene Wörterbuchargumente mit derselben Funktionalität aufnehmen kann. Es ist besser, x.update (y) nicht unter dem Lambda zu verwenden, da immer None zurückgegeben wird . Und ich versuche, eine allgemeinere Funktion merge_with zu schreiben , die verschiedene Wörterbuchargumente verwendet und mit der angegebenen Funktion doppelte Schlüssel behandelt. Sobald ich fertig bin, werde ich es in einem anderen Thread veröffentlichen, in dem die Lösung relevanter ist.
Lei Zhao
Hier ist der Link, unter dem ich die allgemeinere Lösung geschrieben habe. Willkommen und schauen Sie.
Lei Zhao
1

Beginnend erstellt Python 3.9der Bediener |ein neues Wörterbuch mit den zusammengeführten Schlüsseln und Werten aus zwei Wörterbüchern:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d3 = d2 | d1
# d3: {'b': 2, 'c': 3, 'a': 1}

Dies:

Erstellt ein neues Wörterbuch d3 mit den zusammengeführten Schlüsseln und Werten von d2 und d1. Die Werte von d1 haben Priorität, wenn sich d2 und d1 Schlüssel teilen.


Beachten Sie auch den |=Operator, der d2 durch Zusammenführen von d1 mit Priorität für d1-Werte ändert:

# d1 = { 'a': 1, 'b': 2 }
# d2 = { 'b': 1, 'c': 3 }
d2 |= d1
# d2: {'b': 2, 'c': 3, 'a': 1}

Xavier Guihot
quelle
0

Ich glaube, dass, wie oben erwähnt, die Verwendung d2.update(d1)der beste Ansatz ist und dass Sie auch d2zuerst kopieren können, wenn Sie es noch benötigen.

Obwohl ich darauf hinweisen möchte, dass dies dict(d1, **d2)im Allgemeinen eine schlechte Methode zum Zusammenführen von Wörterbüchern ist, da Schlüsselwortargumente Zeichenfolgen sein müssen, schlägt dies fehl, wenn Sie Folgendes haben dict:

{
  1: 'foo',
  2: 'bar'
}
Olivier Melançon
quelle