Pytanie Sprawdź, czy dany klucz już istnieje w słowniku


Chciałem sprawdzić, czy klucz istnieje w słowniku przed aktualizacją wartości klucza. Napisałem następujący kod:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

Myślę, że nie jest to najlepszy sposób na wykonanie tego zadania. Czy istnieje lepszy sposób na przetestowanie klucza w słowniku?


2189
2017-10-21 19:05


pochodzenie


Powołanie dict.keys() tworzy listę kluczy, zgodnie z dokumentacją docs.python.org/2/library/stdtypes.html#dict.keys ale byłbym zaskoczony, gdyby ten wzorzec nie był zoptymalizowany, aby w poważnej implementacji tłumaczyć if 'key1' in dict:. - Evgeni Sergeev
W końcu dowiedziałem się, dlaczego wiele moich skryptów w Pythonie było tak powolnych :) :( To dlatego, że używałem x in dict.keys() sprawdzić klucze. I stało się tak, ponieważ zwykłym sposobem na iterowanie kluczy w Javie jest for (Type k : dict.keySet()), ten nawyk powoduje for k in dict.keys() czuć się bardziej naturalnie niż for k in dict (które powinno nadal być w porządku pod względem wydajności?), ale wtedy sprawdza się kluczowanie if k in dict.keys() też, co jest problemem ... - Evgeni Sergeev
@EvgeniSergeev if k in dict_: testy na obecność k w kluczach Dict_, więc nadal nie potrzebujesz dict_.keys(). (To mnie ugryzło, ponieważ brzmi dla mnie jak testowanie wartość w dykt. Ale tak nie jest.) - ToolmakerSteve
@ToolmakerSteve To prawda, ale nie tylko tego nie potrzebujesz, to nie jest dobra praktyka. - Evgeni Sergeev
Spróbuj "wpisać klucz" - marcelosalloum


Odpowiedzi:


in jest zamierzonym sposobem na sprawdzenie istnienia klucza w dict.

d = dict()

for i in xrange(100):
    key = i % 10
    if key in d:
        d[key] += 1
    else:
        d[key] = 1

Jeśli chcesz mieć wartość domyślną, zawsze możesz użyć dict.get():

d = dict()

for i in xrange(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

... i jeśli chcesz zawsze zapewnić wartość domyślną dla każdego klawisza, którego możesz użyć defaultdict z collections moduł, jak na przykład:

from collections import defaultdict

d = defaultdict(lambda: 0)

for i in xrange(100):
    d[i % 10] += 1

... ale ogólnie rzecz biorąc in słowo kluczowe to najlepszy sposób na zrobienie tego.


2242
2017-10-21 19:10



Zwykle po prostu używam get jeśli i tak zamierzam wyciągnąć przedmiot ze słownika. Nie ma sensu używać in  i wyciągając przedmiot ze słownika. - Jason Baker
W pełni się zgadzam. Ale jeśli potrzebujesz tylko wiedzieć, czy klucz istnieje, lub musisz rozróżnić między przypadkiem, w którym klucz jest zdefiniowany, a przypadkiem, w którym używasz wartości domyślnej, in to najlepszy sposób na zrobienie tego. - Chris B.
Odniesienie dla tej odpowiedzi jest w dokumentacji Pythona - enkash
get jest złym testem, jeśli klucz jest równoważny "Fałsz", np 0 na przykład. Nauczyłem się tego na własnej skórze: / - Sebastien
Nie mogę zgodzić się, że jest to pełna odpowiedź, ponieważ nie wspomniano, że "try" - "z wyjątkiem" będzie najszybszy, gdy liczba kluczy zawodzi jest wystarczająco mała. Zobacz tę odpowiedź poniżej: stackoverflow.com/a/1602945/4376643 - Craig Hicks


Nie musisz dzwonić:

if 'key1' in dict:
  print "blah"
else:
  print "boo"

To będzie dużo szybciej ponieważ używa on skrótu słownikowego, a nie wyszukiwania liniowego, które wykonałyby klawisze wywołujące.


1099
2017-10-21 19:06



To wspaniale. Miałem wrażenie, że wewnętrznie nadal będzie przechodzić przez listę kluczy, ale widzę, że działa to bardziej jak testowanie członkostwa w zestawie. - Mohan Gulati
@Mohan Gulati: Rozumiesz, że słownik jest hashtable z kluczami mapowanymi na wartości, prawda? Algorytm mieszania konwertuje klucz na liczbę całkowitą, a liczba całkowita służy do znajdowania lokalizacji w zgodnej tabeli mieszania. pl.wikipedia.org/wiki/Hash_table - hughdbrown
@Charles Addis, dzięki doświadczeniu z około pół miliona kluczy zyskujesz co najmniej 10-krotny wzrost wydajności podczas pisania "key in dict" zamiast "key in dict.keys ()". PEP i Zen również stwierdzają, że należy je zignorować, na wypadek, gdyby były złe dla twojego projektu. - ivan_bilan
ivan_bilan - Właśnie przeprowadziłem swój test na tym ... Na pół miliona kluczy, if key in d1 wziął 0.17265701293945312 sekundy. Powołanie if key in d1.keys() wziął 0.23871088027954102 - jest to klasyczna definicja mikrooptymalizacji. Oszczędność 0.07884883880615234 sekundy to nie zwiększenie wydajności. - Charles Addis
@ Eli Dla Ciebie stworzyłem test, który sam możesz wykonać. Wyniki mogą cię zadziwić. Do dyktowania z ~ 50 000 kluczy, nie dzwonienie keys() daje 0,01 drugiej korzyści obliczeniowej. Dla ~ 500 000 kluczy, nie dzwonienie keys() daje ci drugą korzyść .1. Dla ~ 5 000 000 kluczy, nie dzwonienie keys() jest o 4 sekundy szybsze, ale dla 50 000 000 kluczy POWOŁANIE keys() JEST TO 3 SEKUNDY SZYBCIEJ! - Charles Addis


Możesz przetestować obecność klucza w słowniku, korzystając z w słowo kluczowe:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

Powszechnym zastosowaniem sprawdzania istnienia klucza w słowniku przed jego mutacją jest ustawienie domyślne - zainicjuj wartość (np. Jeśli na przykład twoje wartości są listami i chcesz upewnić się, że istnieje pusta lista, do której możesz dodać podczas wstawiania pierwszej wartości dla klucza). W takich przypadkach możesz znaleźć collections.defaultdict() wpisz, które Cię interesują.

W starszym kodzie możesz również znaleźć niektóre zastosowania has_key(), przestarzała metoda sprawdzania istnienia kluczy w słownikach (wystarczy użyć key_name in dict_name, zamiast).


226
2017-10-21 19:16



dict.has_key (key) został uznany za przestarzały na rzecz key in dict - David Locke
Technicznie, has_key jest przestarzałe dla Python 2.x + (nie tylko dla wersji 3.0+). Oznacza to, że nowy kod jest zalecany, aby go nie używać, nawet podczas pisania w Pythonie 2.x. (Ponieważ jest to funkcja, o której wiadomo, że odejdzie w przyszłych wersjach, a zamiast tego jest całkiem niezły substytut). Co się dzieje w wersji 3.0, to że jest całkowicie usunięty. - ToolmakerSteve
@ToolmakerSteve Jesteś oczywiście poprawny i zaktualizowałem odpowiedź, aby to odzwierciedlić. :) - kqr
Chciałem się tym dzielić (używając Pythona 2.7) czasem, w którym napisałem coś, opierając się głównie na dyktach, było 363.235070 używając "key in dict.keys ()" i drastycznie zszedł do 0.260186 wyłącznie poprzez usunięcie wywołania "keys ( ) " - Ido_f
@Ido_f proszę zamieścić swoje testy porównawcze, ponieważ moje testy porównawcze prawie nie różnią się w wersjach 3.5 i 2.7 - Charles Addis


Możesz to skrócić:

if 'key1' in dict:
    ...

Jest to jednak w najlepszym wypadku poprawa kosmetyczna. Dlaczego uważasz, że to nie jest najlepszy sposób?


74
2017-10-21 19:06



To jest dużo więcej niż kosmetyczna poprawa. Czas znalezienia klucza za pomocą tej metody to O (1), natomiast klawisze wywołujące wygenerują listę i będą O (n). - Jason Baker
O (1) nie wydaje się całkiem poprawne. Czy na pewno nie jest to coś w rodzaju O (log n)? - spectras
Jest to złożoność pojedynczego wyszukiwania dyktującego, które jest średnio O (1), aw najgorszym O (n). .list () zawsze będzie O (n). wiki.python.org/moin/TimeComplexity - Leo Tindall


Polecam używanie setdefault zamiast tego metoda. Wygląda na to, że zrobi wszystko, co chcesz.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

40
2017-10-21 19:07



Co robi setdefault co zrobić z pytaniem PO? - hughdbrown
@hughdbrown "Chciałem sprawdzić, czy klucz istnieje w słowniku przed aktualizacją wartości klucza." Czasami posty zawierają kod, który generuje lawinę odpowiedzi na coś, co nie jest oryginalnym celem. Aby osiągnąć cel określony w pierwszym zdaniu, setdefault jest najskuteczniejszą metodą, nawet jeśli nie jest zamiennikiem wstawianego kodu. - David Berger
Jest to najlepsza odpowiedź, ponieważ spełnia ona cel OP, a nie tylko technicznie poprawną odpowiedź. Widzieć: nedbatchelder.com/blog/201207/... - Niels Bom
+1 za odpowiedź informacyjną, która mnie czegoś nauczyła. Jednak to, czy jest to najlepsze rozwiązanie, zależy od tego, co ma na myśli programista; na przykład znaczenie "przed aktualizacją wartości klucza". Może on rzuci wyjątek, jeśli go nie ma (== nie ma uprawnień do dodawania nowych kluczy). Może jest to słownik zliczeń, a on doda 1 do istniejącej liczby, w którym to przypadku `d [klucz] = d.get (klucz, 0) + 1 'jest najczystszym rozwiązaniem (jak pokazuje Chris po twojej odpowiedzi było napisane). (Tylko o tym wspomniałem, na wypadek gdyby przyszli czytelnicy przyszli tutaj, mając na uwadze różne zadania.) - ToolmakerSteve
@NielsBom ... IMHO setdefault jest tylko  lepszy rozwiązanie, gdy istniejący wpis powinien nie być nadpisane. (Ważny przypadek, ale nie jedyny powód, aby testować istnienie klucza). - ToolmakerSteve


Aby uzyskać dodatkowe informacje na temat szybkości realizacji metod proponowanych przez zaakceptowaną odpowiedź (pętle 10m):

  • 'key' in mydict upłynął czas 1,07 sec
  • mydict.get('key') upłynął czas 1,84 sek
  • mydefaultdict['key'] upłynął czas 1,07 sec

Dlatego używaj in lub defaultdict są zalecane od get.


35
2018-05-29 11:06



get jest w istocie połączeniem punktów 1 i 3. - scape
całkowicie się z tym zgadzam get1,84 s to <1,07 * 2 ;-P - Paul Rigor


Słownik w Pythonie ma metodę get ('key', default). Możesz więc ustawić domyślną wartość na wypadek, gdyby nie było klucza.

values = {...}
myValue = values.get('Key', None)

19
2018-03-01 09:03





Do sprawdzenia możesz użyć has_key() metoda

if dict.has_key('key1'):
   print "it is there"

Jeśli chcesz wartość, możesz użyć get() metoda

a = dict.get('key1', expeced_type)

Jeśli chcesz, aby krotka, lista lub słownik lub dowolny ciąg znaków był wartością domyślną jako wartością zwracaną, użyj get() metoda

a = dict.get('key1', {}).get('key2', [])

15
2017-09-10 18:37



.get i has_key zostały już zasugerowane w odpowiedziach lat przed twoimi, has_key również został usunięty w python3 - Padraic Cunningham


Korzystanie z operatora trójskładnikowego:

message = "blah" if 'key1' in dict else "booh"
print(message)

14
2017-08-18 22:58





A co z wykorzystaniem EAFP (łatwiej poprosić o przebaczenie niż pozwolenie):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

Zobacz inne wpisy SO:

Używanie try vs jeśli w python lub

Sprawdzanie istnienia członków w Pythonie


14
2018-02-06 16:08



Try / except może być droższy, jeśli jest prawdopodobne, że klucz często nie istnieje. Z wpisu, do którego się odwołałeś: "[Oczekuję, że 99% wyniku czasu będzie zawierało coś, co jest niemożliwe do sprawdzenia, użyłbym metody try / except, która będzie szybsza, jeśli wyjątki są naprawdę wyjątkowe. więcej niż 50% czasu, a następnie użycie, jeśli jest prawdopodobnie lepsze. [...] [A] n, jeśli instrukcja zawsze Cię kosztuje, prawie za darmo możesz ustawić blok try / except, ale gdy wystąpi wyjątek, Koszt jest znacznie wyższy. " stackoverflow.com/a/1835844/1094092 - billrichards
Zamiast gołego połowu określiłem except KeyError tutaj. - shuttle87


Tylko FYI dodając do Chrisa. B (najlepsza odpowiedź):

d = defaultdict(int)

Działa również; Powodem jest to, że dzwonisz int() zwraca 0 które są Czym defaultdict działa za kulisami (przy konstruowaniu słownika), stąd nazwa "Funkcja fabryczna" w dokumentacji.


13
2018-05-19 18:12



(Dałem ci +1, ponieważ Chris " defaultdict(lambda: 0) wydawał mi się niejasny. mówiąc "jest to słownik int, więc zaczynają się od wartości domyślnej int, np. int () np. 0" lubię. " - ToolmakerSteve
Jeśli tworzysz słownik zliczeń, powinieneś używać Licznik (zakładając, że Python 2.7). I użyłem defaultdict(lambda: 0) zamiast defaultdict(int) ponieważ myślę, że jest jaśniejsze, co się dzieje; Czytelnik nie musi wiedzieć, że dostajesz 0 jeśli zadzwonisz int() bez argumentów. YMMV. - Chris B.