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

2683

Chciałem sprawdzić, czy klucz istnieje w słowniku przed zaktualizowaniem 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 jest lepszy sposób na sprawdzenie klucza w słowniku?

Mohan Gulati
źródło
31
Wywołanie dict.keys()tworzy listę kluczy, zgodnie z dokumentacją docs.python.org/2/library/stdtypes.html#dict.keys, ale zdziwiłbym się, gdyby ten wzorzec nie został zoptymalizowany pod kątem poważnej implementacji do przetłumaczenia do if 'key1' in dict:.
Evgeni Sergeev,
7
Więc w końcu okazało się, dlaczego wielu z moich skryptów Pythona były tak powolne :) :(. To dlatego, że używam x in dict.keys()do sprawdzania kluczy. I tak się stało, ponieważ zwykły sposób iteracyjne nad klucze w Javie jest for (Type k : dict.keySet())to zwyczaj powodując for k in dict.keys()do czujesz się bardziej naturalnie niż for k in dict(co powinno być w porządku pod względem wydajności?), ale potem staje się if k in dict.keys()również sprawdzanie kluczy , co jest problemem ...
Evgeni Sergeev
4
@EvgeniSergeev if k in dict_:sprawdza obecność k w KLUCZACH dict_, więc nadal nie potrzebujesz dict_.keys(). (To mnie ugryzło, ponieważ czytało mi się, jakby testowało wartość w dyktando. Ale tak nie jest.)
ToolmakerSteve
1
@ToolmakerSteve Zgadza się, ale nie tylko nie jest to potrzebne, ale nie jest to dobra praktyka.
Evgeni Sergeev
26
Spróbuj „wprowadzić klucz”
marcelosalloum

Odpowiedzi:

3366

injest zamierzonym sposobem sprawdzenia istnienia klucza w pliku dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

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

d = dict()

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

a jeśli chcesz zawsze zapewnić domyślną wartość dla dowolnego klucza, którego możesz użyć dict.setdefault()wielokrotnie lub defaultdictz collectionsmodułu, na przykład:

from collections import defaultdict

d = defaultdict(int)

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

ale ogólnie insłowo kluczowe jest najlepszym sposobem na to.

Chris B.
źródło
74
Zwykle używam tylko, getjeśli i tak będę wyciągać element ze słownika. Nie ma sensu używać in i wyciągać elementu ze słownika.
Jason Baker
75
W pełni się zgadzam. Ale jeśli potrzebujesz tylko wiedzieć, czy klucz istnieje, lub musisz rozróżnić przypadek, w którym klucz jest zdefiniowany, a przypadek, w którym używasz wartości domyślnej, into najlepszy sposób na zrobienie tego.
Chris B.
5
Odniesienie do tej odpowiedzi znajduje się w dokumentacji Pythona
enkash
30
get jest złym testem, jeśli klucz jest równoważny z „False”, jak 0na przykład. Nauczyłem się tego na
Sebastien
4
Nie mogę się zgodzić, że jest to pełna odpowiedź, ponieważ nie wspomina, że ​​„spróbuj” - „z wyjątkiem” będzie najszybsze, gdy liczba awarii klucza będzie wystarczająco mała. Zobacz odpowiedź poniżej: stackoverflow.com/a/1602945/4376643
Craig Hicks
1546

Nie musisz dzwonić na klucze:

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

Będzie to znacznie szybsze, ponieważ używa haszowania słownika w przeciwieństwie do wyszukiwania liniowego, co robiłyby klawisze wywoływania.

Jason Baker
źródło
7
To wspaniale. Miałem wrażenie, że wewnętrznie nadal przejdzie przez listę kluczy, ale widzę, że działa to bardziej jak testowanie członkostwa w zestawie.
Mohan Gulati,
51
@Mohan Gulati: Rozumiesz, że słownik jest skrótem kluczy odwzorowanych na wartości, prawda? Algorytm mieszający konwertuje klucz na liczbę całkowitą, a liczba całkowita służy do znalezienia pasującej lokalizacji w tabeli skrótów. en.wikipedia.org/wiki/Hash_table
hughdbrown
5
@Charles Addis, z doświadczenia w pracy z około pół milionem klawiszy uzyskujesz przynajmniej 10-krotny wzrost wydajności, pisząc „klucz w dict” zamiast „klucz w dict.keys ()”. PEP i Zen stwierdzają również, że powinieneś je zignorować, jeśli są szkodliwe dla twojego projektu.
ivan_bilan
11
ivan_bilan - Właśnie przeprowadziłem własny test na tym ... Na pół miliona kluczy if key in d1zajęło mi 0.17265701293945312kilka sekund. Powołanie if key in d1.keys()trwało 0.23871088027954102- to klasyczna definicja mikro optymalizacji. Oszczędzanie 0.07884883880615234sekund nie zwiększa wydajności.
Charles Addis,
11
@Eli Dla Ciebie stworzyłem test, który możesz sam uruchomić. Wyniki mogą Cię zaskoczyć. W przypadku nagrań z ~ 50 000 kluczy, brak połączenia keys()daje 0,01 sekundy korzyści obliczeniowej. Za ~ 500 000 kluczy, brak połączenia keys()daje 1 sekundę korzyści. Dla ~ 5 000 000 kluczy, brak połączenia keys()jest o 0,4 sekundy szybszy, ale dla 50 000 000 kluczy WYWOŁANIE keys()JEST 3 SEKUNDY SZYBCIEJ!
Charles Addis,
268

Możesz sprawdzić obecność klucza w słowniku, używając słowa kluczowego in :

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

Typowym zastosowaniem do sprawdzania istnienia klucza w słowniku przed jego mutacją jest domyślna inicjalizacja wartości (np. Jeśli twoje wartości są na przykład listami i chcesz upewnić się, że istnieje pusta lista, do której możesz dołączyć podczas wstawiania pierwszej wartości klucza). W takich przypadkach może okazać się interesujący collections.defaultdict()typ.

W starszym kodzie można również znaleźć niektóre zastosowania has_key()przestarzałej metody sprawdzania istnienia kluczy w słownikach (wystarczy użyć key_name in dict_namezamiast tego).

Michael Aaron Safyan
źródło
2
Chciałem podzielić się tym (używając Pythona 2.7) z czasem, w którym napisałem, w dużej mierze na podstawie nagrań, 363.235070 przy użyciu „key in dict.keys ()” i drastycznie spadłem do 0.260186 wyłącznie poprzez usunięcie wezwania do „kluczy ( ) ”
Ido_f
@Ido_f, proszę opublikuj swoje testy porównawcze, ponieważ moje testy porównawcze prawie nie różnią się w wersjach 3.5 i 2.7
Charles Addis
@Ido_f Podejrzewam, że było to coś innego w twoim programie, co było czymś innym, ale w rzeczywistości nie key in dict.keys(). Spróbuj usunąć cały kod z wyjątkiem tej kontroli i sprawdź, jaki jest twój wynik.
Charles Addis,
101

Możesz to skrócić:

if 'key1' in dict:
    ...

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

Greg Hewgill
źródło
100
To znacznie więcej niż kosmetyczna poprawa. Czas na znalezienie klucza przy użyciu tej metody to O (1), podczas gdy wywołanie kluczy wygeneruje listę i będzie O (n).
Jason Baker
5
O (1) nie wydaje się całkiem poprawne. Czy na pewno nie jest to coś takiego jak O (log n)?
spectras
12
Jest to złożoność wyszukiwania pojedynczego dykta, który wynosi średnio O (1), aw najgorszym przypadku O (n). .list () zawsze będzie O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall
1
pozwala to również uniknąć dodatkowego przydziału. (ważne, aby ciasne pętle były nieco szybsze)
nurettin
56

Aby uzyskać dodatkowe informacje na temat szybkości wykonywania proponowanych metod przyjętej odpowiedzi (pętle 10 m):

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

Dlatego przy użyciu inlub defaultdictsą zalecane przeciwko get.

Wtower
źródło
6
całkowicie zgadzam się, że get1,84 to <1,07 * 2 ;-P
Paul Rigor
54

setdefaultZamiast tego poleciłbym użycie tej metody. 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}
David Berger
źródło
9
Co to setdefaultma wspólnego z pytaniem PO?
hughdbrown
18
@hughdbrown „Chciałem sprawdzić, czy klucz istnieje w słowniku przed zaktualizowaniem wartości klucza”. Czasami posty zawierają kod, który generuje lawinę odpowiedzi na coś, co nie jest do końca pierwotnym celem. Aby osiągnąć cel określony w pierwszym zdaniu, setdefault jest najskuteczniejszą metodą, nawet jeśli nie zastępuje on wstawionego przykładowego kodu.
David Berger
5
To najlepsza odpowiedź, ponieważ spełnia cel OP, a nie tylko technicznie poprawną odpowiedź. Zobacz: nedbatchelder.com/blog/201207/…
Niels Bom
+1 za pouczającą odpowiedź, która mnie czegoś nauczyła. Jednak to, czy jest to najlepsze rozwiązanie, zależy od tego, co programista ma na myśli; np. znaczenie „przed aktualizacją wartości klucza”. Może rzuci wyjątek, jeśli go nie ma (== brak pozwolenia na dodawanie 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). (Wspomnę tylko o tym, na wypadek, gdyby przyszli czytelnicy przyszli tutaj z myślą o różnych zadaniach.)
ToolmakerSteve,
1
@ToolmakerSteve True. Problem polega na tym, że pytanie OP nie było wystarczająco jasne.
Niels Bom,
45

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

values = {...}
myValue = values.get('Key', None)
mafonya
źródło
33

Co z użyciem EAFP (łatwiej poprosić o wybaczenie niż zgodę):

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

Zobacz inne posty SO:

Używanie try vs, jeśli w Pythonie lub

Sprawdzanie istnienia członka w Pythonie

HungryArthur
źródło
12
Spróbuj / z wyjątkiem może być droższy, jeśli jest prawdopodobne, że klucz często nie istnieje. Z postu, do którego się odwołujesz: „[Jeśli] oczekujesz, że 99% czasu rzeczywiście będzie zawierało coś, co da się powtórzyć, zastosowałbym podejście try / wyjątek. Będzie to szybsze, jeśli wyjątki naprawdę będą wyjątkowe. Jeśli wynikiem jest Brak więcej niż 50% czasu, wtedy użycie if jest prawdopodobnie lepsze. [...] [A] Jeśli instrukcja zawsze cię kosztuje, prawie można ustawić blok try / try. Ale kiedy faktycznie wystąpi wyjątek, koszt jest znacznie wyższy ”. stackoverflow.com/a/1835844/1094092
billrichards
28

Za pomocą operatora trójskładnikowego:

message = "blah" if 'key1' in dict else "booh"
print(message)
Charitoo
źródło
20

Sposoby uzyskania wyników to:

  • if your_dict.has_key (key) Usunięte w Python 3
  • jeśli wpisz swój_dykt
  • spróbuj / z wyjątkiem bloku

To, co jest lepsze, zależy od 3 rzeczy:

  1. Czy słownik „zwykle ma klucz”, czy „zwykle nie ma klucza”.
  2. Czy zamierzasz stosować warunki, takie jak jeśli ... else ... elseif ... else?
  3. Jak duży jest słownik?

Czytaj więcej: http://paltman.com/try-except-performance-in-python-a-simple-test/

Użycie try / block zamiast „in” lub „if”:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."
Bishwas Mishra
źródło
2
Dobry, ale wymaga aktualizacji dla Pythona 3. Przekonwertowałem skrypt strony internetowej 2to3i zauważyłem, że składnia bez try jest zawsze szybsza niż składnia z try, nawet w przypadku, gdy klucz znajduje się w dykcie.
Jean Paul
18

Tylko Python 2: (i Python 2.7 injuż obsługuje )

możesz użyć metody has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass
wan kenobi
źródło
22
.has_key()został uznany za przestarzały ; powinieneś używać, injak pokazano w innych odpowiedziach.
Brad Koch
12
BTW, polecam przeczytać WSZYSTKIE istniejące odpowiedzi na stare pytanie, zanim na nie odpowiem. Ta odpowiedź nic nie dodała, ponieważ sugestia istniała już w odpowiedzi Michaela z '09. (Nie chcę zniechęcać do próby dodania czegoś przydatnego do dyskusji. Próbuj dalej.)
ToolmakerSteve
16

Tylko dodatkowa informacja dla Chrisa. B (najlepsza odpowiedź):

d = defaultdict(int)

Działa również; Powodem jest to, że wywoływanie int()zwraca 0to, co defaultdictrobi za kulisami (podczas konstruowania słownika), stąd nazwa „Funkcja fabryczna” w dokumentacji.

Mauricio Morales
źródło
2
Jeśli tworzysz słownik zliczeń, powinieneś używać Counter (zakładając Python 2.7). I użyłem defaultdict(lambda: 0)zamiast tego, defaultdict(int)ponieważ myślę, że jest bardziej zrozumiałe, co się dzieje; czytelnik nie musi wiedzieć, że dostajesz, 0jeśli dzwonisz int()bez argumentów. YMMV.
Chris B.
9

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

Aby dowiedzieć się, jak to zrobić, najpierw sprawdzamy, jakie metody możemy wywołać w słowniku. Oto metody:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

Brutalna metoda sprawdzenia, czy klucz już istnieje, może być następującą get()metodą:

d.get("key")

Pozostałe dwie interesujące metody items()i keys()brzmią jak zbyt dużo pracy. Sprawdźmy więc, czy get()jest to dla nas właściwa metoda. Mamy nasz dyktand d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Drukowanie pokazuje, że klucz, którego nie mamy, powróci None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

My może używać, aby uzyskać informacje, czy klucz jest obecny lub nie. Ale rozważ to, jeśli utworzymy dyktat z jednym key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Prowadzenie tej get()metody nie jest wiarygodne w przypadku, gdy niektóre wartości mogą być None. Ta historia powinna mieć szczęśliwsze zakończenie. Jeśli użyjemy inkomparatora:

print('key' in d) #True
print('key2' in d) #False

Otrzymujemy prawidłowe wyniki. Możemy zbadać kod bajtowy Pythona:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

To pokazuje, że inoperator porównania jest nie tylko bardziej niezawodny, ale nawet szybszy niż get().

prosti
źródło
.get()może mieć drugi argument defaultwartości, który może być wykorzystany do rozwiązania problemu gdzie key:None. przykład: d.get("key", False)
Alex
.get()to najszybszy sposób. Inną opcją jest przypisanie w bloku try/except
HCLivess
7

Słownik Python ma metodę o nazwie __contains__. Ta metoda zwróci True, jeśli słownik ma klucz, a inna zwróci False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.
Siva Gnanam
źródło
2
Dzwonienie bezpośrednio jest bardzo złą praktyką __contains__. Prawidłowym sposobem na to jest użycie inoperatora, który containment checkwywołuje __contains__funkcję.
user1767754
@ user1767754 Używam foo = x['foo'] if x.__contains__('foo') else 'bar'. Wszelkie pomysły, w jaki sposób można użyć inoperatora jako części tego wyrażenia?
donrondadon
1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu
5

Udostępnianie jeszcze jednego sposobu sprawdzania, czy klucz istnieje za pomocą operatorów logicznych.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

To zwraca

>>> blah
>>> blah
>>> boo
>>> boo

Wyjaśnienie

Po pierwsze należy wiedzieć, że w Pythonie 0, Nonelub obiektów o zerowej długości do oceny False. Wszystko inne ocenia True. Operacje logiczne są oceniane od lewej do prawej i zwracają operand nie Prawda lub Fałsz.

Zobaczmy przykład:

>>> 'Some string' or 1/0 
'Some string'
>>>

Ponieważ 'Some string'ocenia na True, reszta ornie jest oceniana i nie występuje podział przez błąd zerowy.

Ale jeśli zmienimy, 1/0najpierw oceniane jest zamówienie i pojawia się wyjątek:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

Możemy użyć tego do wzorca do sprawdzenia, czy klucz istnieje.

(k in d and 'blah')

robi to samo co

if k in d:
    'blah'
else:
    False

To już zwraca poprawny wynik, jeśli klucz istnieje, ale chcemy, aby wypisał „boo”, gdy nie istnieje. Tak więc bierzemy wynik i orto z'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 
J. Antunes
źródło
1

Możesz użyć forpętli do iteracji w słowniku i uzyskać nazwę klucza, który chcesz znaleźć w słowniku, a następnie sprawdź, czy istnieje, czy nie używa ifwarunku:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')
Abdulkalek
źródło
sprawdź kod, ponieważ wynikiem tego jest it is existinot exist
system123456
po co w ogóle używać słownika, jeśli ma to na celu wyszukiwanie liniowe?
Jean-François Fabre