Wie initialisiere ich ein Wörterbuch mit leeren Listen in Python?

85

Bei meinem Versuch, ein Listenwörterbuch programmgesteuert zu erstellen, kann ich Wörterbuchschlüssel nicht einzeln adressieren. Immer wenn ich das Listenwörterbuch erstelle und versuche, es an einen Schlüssel anzuhängen, werden alle aktualisiert. Hier ist ein sehr einfacher Testfall:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Tatsächliche Ergebnis: {0: ['hello'], 1: ['hello']}

Erwartetes Ergebnis: {0: [], 1: ['hello']}

Hier ist was funktioniert

data = {0:[],1:[]}
data[1].append('hello')
print data

Tatsächliches und erwartetes Ergebnis: {0: [], 1: ['hello']}

Warum fromkeysfunktioniert die Methode nicht wie erwartet?

Martin Burch
quelle

Antworten:

108

Die Übergabe []als zweites Argument dict.fromkeys()ergibt ein ziemlich nutzloses Ergebnis - alle Werte im Wörterbuch sind dasselbe Listenobjekt.

In Python 2.7 oder höher können Sie stattdessen ein dicitonäres Verständnis verwenden:

data = {k: [] for k in range(2)}

In früheren Versionen von Python können Sie verwenden

data = dict((k, []) for k in range(2))
Sven Marnach
quelle
2
Nun, das ist ein ziemlich unintuitives Verhalten. Gibt es eine Idee, warum für alle Schlüssel dasselbe Objekt verwendet wird?
Bar
2
@Bar Da es nichts anderes gibt, was die Funktion innerhalb der Semantik der Python-Sprache tun könnte. Sie übergeben ein einzelnes Objekt, das als Wert für alle Schlüssel verwendet werden soll, sodass ein einzelnes Objekt für alle Schlüssel verwendet wird. Es wäre besser, wenn die fromkeys()Methode stattdessen eine Factory-Funktion akzeptiert, damit wir sie listals Funktion übergeben können und diese Funktion für jeden erstellten Schlüssel einmal aufgerufen wird, aber das ist nicht die eigentliche API von dict.fromkeys().
Sven Marnach
3
Dies ist überhaupt nicht intuitiv. Ich brauchte eine Stunde, um es zu finden. Danke
Astrid
Dasselbe passiert, wenn Sie dict () als zweites Argument übergeben. Sehr verwirrendes Verhalten.
Orly
@Orly Dies liegt daran, dass zuerst ein leeres Wörterbuch erstellt wird und dann ein Verweis darauf an alle Initialisierungen übergeben wird.
Dr_Zaszuś
81

Verwenden Sie stattdessen defaultdict :

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

Auf diese Weise müssen Sie nicht alle Schlüssel, die Sie für Listen verwenden möchten, vorher initialisieren.

In Ihrem Beispiel verwenden Sie eine (veränderbare) Liste:

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

würde ausgeben {0: [1, 2], 1: [1, 2]}.

Martijn Pieters
quelle
2
In meinem Fall muss ich alle Schlüssel vorher initialisieren, damit der Rest der Programmlogik wie erwartet funktioniert. Andernfalls wäre dies eine gute Lösung. Vielen Dank.
Martin Burch
Ich denke, was in dieser Antwort fehlt, ist zu sagen, dass diese Lösung im Gegensatz zu der des OP funktioniert, weil listhier keine leere Liste, sondern ein Typ ist (oder Sie können es als aufrufbaren Konstruktor sehen, denke ich). Jedes Mal, wenn ein fehlender Schlüssel übergeben wird, wird eine neue Liste erstellt, anstatt denselben erneut zu verwenden.
Dr_Zaszuś
7

Sie füllen Ihre Wörterbücher mit Verweisen auf eine einzelne Liste. Wenn Sie sie aktualisieren, wird die Aktualisierung in allen Verweisen wiedergegeben. Versuchen Sie stattdessen ein Wörterbuchverständnis. Siehe Erstellen eines Wörterbuchs mit Listenverständnis in Python

d = {k : v for k in blah blah blah}
Cobie
quelle
toller Vorschlag zum Initialisieren von Wörterbuchwerten ... danke Cobie! Ich habe Ihr Beispiel erweitert, um die Werte in einem vorhandenen Wörterbuch zurückzusetzen. D. Ich habe dies wie folgt durchgeführt: d = {k: 0 für k in d}
John
Was ist vin dieser Antwort?
Dr_Zaszuś
-2

Sie könnten dies verwenden:

data[:1] = ['hello']
Conner Dassen
quelle
2
Es kann für das OP hilfreich sein, zu erklären, warum dies funktioniert. Die ursprüngliche Frage fragt, warum es nicht wie erwartet funktioniert.
william.taylor.09
@ william.taylor.09 Es ist ziemlich offensichtlich, warum das funktioniert, nicht wahr?
Conner Dassen
OP fragt (war): "Warum funktioniert die fromkeys-Methode nicht wie erwartet?"
william.taylor.09