Python aktualizuje klucz w dict, jeśli nie istnieje


Odpowiedzi:


152

Nie musisz więc dzwonić d.keys(), więc

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

wystarczy. Nie ma jaśniejszej, bardziej czytelnej metody.

Możesz zaktualizować ponownie za pomocą dict.get(), co zwróci istniejącą wartość, jeśli klucz już istnieje:

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

ale zdecydowanie odradzam; jest to kodowanie w golfa, utrudniające konserwację i czytelność.


Czy muszę to zsynchronizować, jeśli mam wiele wątków używających kodu?
ed22

1
@ ed22 tak, ponieważ składa się z wielu instrukcji kodu bajtowego, pomiędzy którymi mogą się przełączać wątki.
Martijn Pieters

84

Zastosowanie 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'}

6
dict.setdefault()powinno być używane tylko podczas próby uzyskania dostępu do wartości.
Martijn Pieters

14
@MartijnPieters: dlaczego to ma znaczenie? Nazwa setdefault()wyraźnie opisuje, co się dzieje - to, że zwraca również wartość, nie jest tak ważne, i trochę dziwaczne IMO. Czy mógłbyś podać szybkie wyjaśnienie lub link do takiego, dlaczego nie jest to pożądane?
mhawke

6
Można by użyć go w wyrażeniu, które przewiduje, że wartość istnieć, podobnie jak w grupie pętli: for obj in iterable: d.setdefault(key_for_obj(obj), []).append(obj). W zwracanej wartości nie ma nic dziwacznego, o to właśnie chodzi w tej metodzie .
Martijn Pieters

2
Ponadto zasłania to, co próbujesz zrobić, czyli ustawić klucz tylko wtedy, gdy nie jest już obecny. Czytelność się liczy, po prostu użyj if key not in d:testu.
Martijn Pieters

7
@MartijnPieters: Dziękuję za wyjaśnienie. Po przeczytaniu dokumentacji setdefault()wydaje się być nastawiony na to, co powiedziałeś. Użycie a defaultdict(list)spowodowałoby, że kod byłby bardziej czytelny niż twój przykład ... więc być może nie ma z niego żadnego pożytku, chyba że trzeba pracować ze standardowymi słownikami. Ogólnie if key not in djest jaśniejszy.
mhawke

25

Od Pythona 3.9 można użyć operatora merge, | aby połączyć dwa słowniki. Dykt po prawej ma pierwszeństwo:

new_dict = old_dict | { key: val }

Na przykład:

new_dict = { 'a': 1, 'b': 2 } | { 'b': 42 }

print(new_dict} # {'a': 1, 'b': 42}

Uwaga: tworzy to nowy słownik ze zaktualizowanymi wartościami.


6
Chociaż jest to interesujące, nie sądzę, że jest bardziej czytelne niż zaakceptowana odpowiedź
Justin Furuness

2
Dla mnie miło jest wiedzieć, że jest teraz sposób na połączenie dwóch dykt
Austin A

To połączenie stanowi wyzwanie. Jeśli klucze są takie same, scalenie zignoruje wartość. Jeśli taki jest zamiar, to działa.
Joe Ferndz

1
@JoeFerndz Dykt po prawej ma pierwszeństwo i nie ignoruje wartości. Zaktualizowałem przykład, aby było to bardziej zrozumiałe.
Rotareti

6

Za pomocą poniższych możesz wstawić wiele wartości, a także mieć wartości domyślne, ale tworzysz nowy słownik.

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

Przetestowałem go z największą liczbą głosowanych odpowiedzi i średnio jest to szybsze, jak widać na poniższym przykładzie.

Test szybkości porównujący metodę opartą na pętli for ze zrozumieniem dyktu z operatorem rozpakowania Test szybkości porównujący metodę opartą na pętli for ze zrozumieniem dyktu z metodą operatora rozpakowania.

jeśli kopia ( d = default_vals.copy()) nie zostanie wykonana w pierwszym przypadku, wówczas odpowiedź z największą liczbą głosów będzie szybsza, gdy osiągniemy rząd wielkości 10**5i więcej. Ślad pamięciowy obu metod jest taki sam.


1
W takim razie po co sugerować to w pierwszej kolejności
jonathan

1
To rozwiązanie wydaje mi się całkowicie uzasadnione. Umożliwia również aktualizację wielu wartości w jednej deklaracji.
Rotareti

Dziękuję bardzo. Ale wydaje się, że powinno być{**default_values, **{ key: value }}
osexp2003

0

Zgodnie z powyższymi odpowiedziami zadziałała u mnie metoda setdefault () .

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}'.")

Chociaż to rozwiązanie nie jest ogólne. Po prostu pasowało mi do tego konkretnego przypadku. Dokładnym rozwiązaniem byłoby sprawdzenie keypierwszego (zgodnie z wcześniejszymi zaleceniami), ale dzięki temu setdefault()unikamy jednego dodatkowego wyszukiwania w słowniku, to znaczy, choć niewielkiego, ale wciąż zwiększającego wydajność.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.