Python aktualisiert einen Schlüssel in dict, wenn er nicht existiert

Antworten:

144

Sie müssen nicht anrufen müssen d.keys(), so

if key not in d:
    d[key] = value

reicht. Es gibt keine klarere und besser lesbare Methode.

Sie können erneut mit aktualisieren dict.get(), wodurch ein vorhandener Wert zurückgegeben wird, wenn der Schlüssel bereits vorhanden ist:

d[key] = d.get(key, value)

aber ich empfehle dringend dagegen; Dies ist Code-Golf, was die Wartung und Lesbarkeit behindert.

Martijn Pieters
quelle
Muss ich dies synchronisieren, wenn ich mehrere Threads mit dem Code habe?
ed22
1
@ ed22 ja, weil es aus mehreren Bytecode-Anweisungen besteht, zwischen denen Threads wechseln können.
Martijn Pieters
77

Verwendung dict.setdefault():

>>> d = {1: 'one'}
>>> d.setdefault(1, '1')
'one'
>>> d    # d has not changed because the key already existed
{1: 'one'}
>>> d.setdefault(2, 'two')
'two'
>>> d
{1: 'one', 2: 'two'}
mhawke
quelle
5
dict.setdefault()sollte eigentlich nur verwendet werden, wenn versucht wird, auch auf den Wert zuzugreifen.
Martijn Pieters
11
@MartijnPieters: Warum ist das wichtig? Der Name setdefault()beschreibt klar, was passiert - dass er auch einen Wert zurückgibt, ist nicht so wichtig und ein wenig skurril IMO. Könnten Sie eine kurze Erklärung oder einen Link zu einer geben, warum dies nicht wünschenswert ist?
Mhawke
6
Sie würden es in einem Ausdruck verwenden, der erwartet, dass ein Wert vorhanden ist, wie in einer Gruppierungsschleife : for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj). Der zurückgegebene Wert ist nicht sonderlich eigenartig, das ist der springende Punkt der Methode .
Martijn Pieters
2
Außerdem wird verdeckt, was Sie versuchen, nämlich einen Schlüssel nur dann festzulegen, wenn er nicht bereits vorhanden ist. Lesbarkeit zählt, verwenden Sie einfach einen if key not in d:Test.
Martijn Pieters
6
@ MartinijnPieters: Danke für die Erklärung. Das Lesen der Dokumentationszeichenfolge setdefault()scheint auf das ausgerichtet zu sein, was Sie gesagt haben. Die Verwendung von a defaultdict(list)würde zu besser lesbarem Code führen als Ihr Beispiel. Vielleicht gibt es keine Verwendung dafür, es sei denn, man muss mit Standardwörterbüchern arbeiten. Insgesamt if key not in dist klarer.
Mhawke
22

Seit 3.9 Python können Sie die verwenden Merge Operator | zwei Wörterbücher zu fusionieren. Das Diktat rechts hat Vorrang:

d = { key: value } | d

Hinweis: Dadurch wird ein neues Wörterbuch mit den aktualisierten Werten erstellt.

Rotareti
quelle
3
Das ist zwar interessant, aber ich denke nicht, dass es lesbarer ist als die akzeptierte Antwort
Justin Furuness,
Für mich ist es nur schön zu wissen, dass es jetzt eine Möglichkeit gibt, zwei Diktate zusammenzuführen
Austin A
5

Mit dem Folgenden können Sie mehrere Werte einfügen und auch Standardwerte haben, aber Sie erstellen ein neues Wörterbuch.

d = {**{ key: value }, **default_values}

Ich habe es mit der am häufigsten bewerteten Antwort getestet und im Durchschnitt ist dies schneller, wie im folgenden Beispiel zu sehen ist.

Geschwindigkeitstest zum Vergleich einer for-Schleifen-basierten Methode mit einem Diktatverständnis mit dem Entpack-Bediener Geschwindigkeitstest zum Vergleich einer for-Schleifen-basierten Methode mit einem Diktatverständnis mit der Entpack-Operator-Methode.

Wenn d = default_vals.copy()im ersten Fall keine Kopie ( ) erstellt wird, ist die am häufigsten gewählte Antwort schneller, sobald wir Größenordnungen von 10**5und mehr erreichen. Der Speicherbedarf beider Methoden ist gleich.

CoderKid
quelle
1
Warum sollte man es dann überhaupt vorschlagen
Jonathan,
1
Diese Lösung scheint mir vollkommen gültig zu sein. Außerdem können mehrere Werte in einer Deklaration aktualisiert werden.
Rotareti
ich danke dir sehr. Aber es sollte sein{**default_values, **{ key: value }}
osexp2003
0

Nach den obigen Antworten hat die Methode setdefault () für mich funktioniert.

old_attr_name = mydict.setdefault(key, attr_name)
if attr_name != old_attr_name:
    raise RuntimeError(f"Key '{key}' duplication: "
                       f"'{old_attr_name}' and '{attr_name}'.")

Diese Lösung ist jedoch nicht generisch. Passte einfach zu mir in diesem bestimmten Fall. Die genaue Lösung wäre, nach dem keyersten zu suchen (wie bereits empfohlen), aber setdefault()wir vermeiden eine zusätzliche Suche im Wörterbuch, das ist zwar klein, aber dennoch ein Leistungsgewinn.

user10815638
quelle