Controlla se una determinata chiave esiste già in un dizionario


2683

Volevo verificare se esiste una chiave in un dizionario prima di aggiornare il valore per la chiave. Ho scritto il seguente codice:

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

Penso che questo non sia il modo migliore per svolgere questo compito. Esiste un modo migliore per testare una chiave nel dizionario?


31
La chiamata dict.keys()crea un elenco di chiavi, secondo la documentazione docs.python.org/2/library/stdtypes.html#dict.keys ma sarei sorpreso se questo modello non fosse ottimizzato per, in una seria implementazione, da tradurre a if 'key1' in dict:.
Evgeni Sergeev,

7
Così ho finalmente trovato il motivo per cui molti dei miei script Python erano così lento :) :(. Ecco perché ho usato x in dict.keys()per controllare per le chiavi. E questo è accaduto perché il solito modo per iterare su chiavi in Java è for (Type k : dict.keySet()), questa abitudine causando for k in dict.keys()a sembra più naturale di for k in dict(cosa dovrebbe ancora andare bene in termini di prestazioni?), ma poi anche il controllo dei tasti diventa if k in dict.keys(), il che è un problema ...
Evgeni Sergeev

4
@EvgeniSergeev if k in dict_:verifica la presenza di k nelle CHIAVI di dict_, quindi non è ancora necessario dict_.keys(). (Questo mi ha morso, dato che mi sembra che stia testando un valore in dict. Ma non lo è.)
ToolmakerSteve

1
@ToolmakerSteve Esatto, ma non solo non ne hai bisogno, non è una buona pratica.
Evgeni Sergeev,

26
Prova "key in dict"
marcelosalloum,

Risposte:


3374

inè il modo previsto per verificare l'esistenza di una chiave in a dict.

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

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

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

Se si desidera un valore predefinito, è sempre possibile utilizzare dict.get():

d = dict()

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

e se si desidera garantire sempre un valore predefinito per qualsiasi chiave, è possibile utilizzare dict.setdefault()ripetutamente o defaultdictdal collectionsmodulo, in questo modo:

from collections import defaultdict

d = defaultdict(int)

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

ma in generale, la inparola chiave è il modo migliore per farlo.


74
Di solito lo uso solo getse sto per estrarre l'elemento dal dizionario comunque. Non ha senso usare in e estrarre l'elemento dal dizionario.
Jason Baker,

75
Sono completamente d'accordo. Ma se hai solo bisogno di sapere se esiste una chiave, o devi distinguere tra un caso in cui la chiave è definita e un caso in cui stai usando un valore predefinito, inè il modo migliore per farlo.
Chris B.

5
Il riferimento per questa risposta è nei documenti di Python
enkash del

30
get è un brutto test se la chiave equivale a "False", come 0ad esempio. L'ho imparato nel modo più duro: /
Sebastien,

4
Non posso essere d'accordo sul fatto che questa sia una risposta completa in quanto non menziona che "provare" - "tranne" sarà il più veloce quando il numero di tasti non riusciti è sufficientemente piccolo. Vedi questa risposta di seguito: stackoverflow.com/a/1602945/4376643
Craig Hicks,

1547

Non è necessario chiamare i tasti:

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

Sarà molto più veloce poiché usa l'hash del dizionario invece di fare una ricerca lineare, come farebbero i tasti di chiamata.


7
È grandioso. Avevo l'impressione che avrebbe ancora attraversato internamente l'elenco di chiavi, ma vedo che funziona più come testare l'appartenenza a un set.
Mohan Gulati,

51
@Mohan Gulati: hai capito che un dizionario è un hashtable di chiavi mappate su valori, giusto? Un algoritmo di hashing converte la chiave in un numero intero e l'intero viene utilizzato per trovare una posizione nella tabella hash che corrisponde. en.wikipedia.org/wiki/Hash_table
hughdbrown,

5
@Charles Addis, dall'esperienza con circa mezzo milione di tasti si ottiene un aumento delle prestazioni di almeno 10 volte quando si scrive "key in dict" anziché "key in dict.keys ()". PEP e Zen affermano inoltre che dovresti ignorarli nel caso in cui siano dannosi per il tuo progetto.
ivan_bilan,

11
ivan_bilan - Ho appena eseguito il mio test da banco su questo ... Con mezzo milione di chiavi, ci sono if key in d1voluti 0.17265701293945312secondi. La chiamata if key in d1.keys()ha richiesto 0.23871088027954102: questa è la classica definizione di micro ottimizzazione. Il salvataggio dei 0.07884883880615234secondi non è un aumento delle prestazioni.
Charles Addis,

11
@Eli Solo per te ho creato un test che puoi eseguire da solo. I risultati potrebbero stupirti. Per i dicts con ~ 50.000 chiavi, non chiamare keys()offre un vantaggio computazionale di 0,01 secondi. Per ~ 500.000 chiavi, non chiamare keys()offre 1 secondo vantaggio. Per ~ 5.000.000 di chiavi, non chiamare keys()è .4 secondi più veloce, ma per 50.000.000 di chiavi CHIAMARE keys()È 3 SECONDI PIÙ RAPIDAMENTE!
Charles Addis,

268

È possibile verificare la presenza di una chiave in un dizionario, utilizzando la a parola chiave:

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

Un uso comune per verificare l'esistenza di una chiave in un dizionario prima di modificarlo è inizializzare il valore per impostazione predefinita (ad esempio, se i valori sono elenchi, ad esempio, e si desidera assicurarsi che sia presente un elenco vuoto a cui è possibile aggiungere quando si inserisce il primo valore per una chiave). In casi come quelli, potresti trovare il collections.defaultdict()tipo di tuo interesse.

Nel codice precedente, potresti anche trovare alcuni usi di has_key()un metodo deprecato per controllare l'esistenza delle chiavi nei dizionari (basta usare key_name in dict_nameinvece).


2
Volevo condividere che (usando Python 2.7) il tempo di esecuzione di qualcosa che ho appena scritto, basandomi pesantemente su dicts, era 363.235070 usando "key in dict.keys ()" e drasticamente sceso a 0.260186 rimuovendo la richiesta di "keys ( ) "
Ido_f,

@Ido_f, per favore pubblica i tuoi benchmark, dato che i miei benchmark non fanno quasi nessuna differenza in 3.5 e 2.7
Charles Addis,

@Ido_f Sospetto che fosse qualcos'altro nel tuo programma che fosse qualcos'altro, ma in realtà no key in dict.keys(). Prova a rimuovere tutto il codice tranne questo controllo e guarda qual è il tuo risultato.
Charles Addis,

101

Puoi abbreviare questo:

if 'key1' in dict:
    ...

Tuttavia, questo è nella migliore delle ipotesi un miglioramento estetico. Perché credi che questo non sia il modo migliore?


100
Questo è molto più di un miglioramento estetico. Il tempo per trovare una chiave usando questo metodo è O (1) mentre chiamare le chiavi genererebbe un elenco ed essere O (n).
Jason Baker,

5
La O (1) non sembra del tutto corretta. Sei sicuro che non sia qualcosa come O (log n)?
extra il

12
È la complessità della ricerca di un singolo dict, che è in media O (1) e nel peggiore dei casi O (n). .list () sarà sempre O (n). wiki.python.org/moin/TimeComplexity
Leonora Tindall,

1
questo evita anche un'allocazione aggiuntiva. (importante per rendere gli anelli stretti un po 'più veloci)
Nurettin

57

Per ulteriori informazioni sull'esecuzione rapida dei metodi proposti per la risposta accettata (loop da 10 m):

  • 'key' in mydict tempo trascorso 1,07 sec
  • mydict.get('key') tempo trascorso 1,84 sec
  • mydefaultdict['key'] tempo trascorso 1,07 sec

Pertanto utilizzare ino defaultdictsono sconsigliati get.


6
concordo getpienamente sul fatto che l '1.84s è <1.07 * 2 ;-P
Paul Rigor il

54

Consiglierei invece di utilizzare il setdefaultmetodo. Sembra che farà tutto quello che vuoi.

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

9
Cosa setdefaultha a che fare con la domanda del PO?
hughdbrown,

18
@hughdbrown "Volevo verificare se esiste una chiave in un dizionario prima di aggiornare il valore per la chiave." A volte i post includono codice che genera una raffica di risposte a qualcosa che non è proprio l'obiettivo originale. Per raggiungere l'obiettivo indicato nella prima frase, setdefault è il metodo più efficace, anche se non è un rimpiazzo di sostituzione per il codice di esempio pubblicato.
David Berger,

5
Questa è la risposta superiore perché soddisfa l'obiettivo di OP invece di dare semplicemente la risposta tecnicamente corretta. Vedi: nedbatchelder.com/blog/201207/…
Niels Bom

+1 per una risposta informativa, che mi ha insegnato qualcosa. Tuttavia, se è la soluzione migliore dipende da ciò che il programmatore ha in mente; ad es. il significato di "prima di aggiornare il valore della chiave". Forse lancerà un'eccezione se non è presente (== nessuna autorizzazione per aggiungere nuove chiavi). Forse è un dizionario di conteggi, e sta per aggiungere 1 al conteggio esistente, nel qual caso `d [key] = d.get (key, 0) + 1 'è la soluzione più pulita (come mostra Chris, dopo la tua risposta fu scritto). (Mi preoccupo solo di menzionarlo, nel caso in cui i futuri lettori vengano qui, con in mente compiti diversi.)
ToolmakerSteve

1
@ToolmakerSteve True. Il problema qui è che la domanda di OP non era abbastanza chiara.
Niels Bom,

45

Il dizionario in python ha un metodo get ('chiave', predefinito). Quindi puoi semplicemente impostare un valore predefinito nel caso in cui non ci sia chiave.

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

33

Che dire dell'utilizzo di EAFP (è più facile chiedere perdono che autorizzazione):

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

Vedi altri post SO:

Usando try vs if in python o

Verifica dell'esistenza dei membri in Python


12
Provare / tranne può essere più costoso se è probabile che la chiave spesso non esista. Dal post a cui hai fatto riferimento: "[I] Se ti aspetti che il 99% delle volte il risultato conterrà effettivamente qualcosa di iterabile, utilizzerei l'approccio try / tranne. Sarebbe più veloce se le eccezioni fossero davvero eccezionali. Se il risultato è Nessuno più del 50% delle volte, quindi utilizzare if è probabilmente migliore. [...] [A] n se l'istruzione ti costa sempre, è quasi gratuito impostare un blocco try / tranne. Ma quando si verifica un'eccezione, il il costo è molto più alto ". stackoverflow.com/a/1835844/1094092
billrichards

28

Utilizzando l'operatore ternario:

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

20

I modi in cui è possibile ottenere i risultati sono:

  • if your_dict.has_key (chiave) Rimosso in Python 3
  • se chiave in your_dict
  • prova / tranne blocco

Quale è meglio dipende da 3 cose:

  1. Il dizionario "normalmente ha la chiave" o "normalmente non ha la chiave".
  2. Intendi utilizzare condizioni come se ... altro ... altro ... altro?
  3. Quanto è grande il dizionario?

Maggiori informazioni: http://paltman.com/try-except-performance-in-python-a-simple-test/

Uso di try / block invece di 'in' o '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."

2
Buono ma deve essere attualizzato per Python 3. Ho convertito lo script della pagina Web con 2to3e ho visto che la sintassi senza tentativo è sempre più veloce della sintassi con tentativo, anche nel caso in cui la chiave sia nel dict.
Jean Paul,

18

Solo Python 2: (e python 2.7 supporta ingià)

puoi usare il metodo has_key ():

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

22
.has_key()è stato deprecato ; dovresti usare income mostrato in altre risposte.
Brad Koch,

12
A proposito, ti consiglio di leggere TUTTE le risposte esistenti a una VECCHIA domanda, prima di rispondere. Questa risposta non aggiunse nulla, poiché il suggerimento esisteva già nella risposta di Michael, dal '09. (Non intendo scoraggiare un tentativo di aggiungere qualcosa di utile a una discussione. Continua a provare.)
ToolmakerSteve

16

Solo una FYI che aggiunge a Chris. B (miglior risposta):

d = defaultdict(int)

Funziona anche; il motivo è che la chiamata int()ritorna 0che è ciò che defaultdictfa dietro le quinte (quando si costruisce un dizionario), da cui il nome "Funzione di fabbrica" ​​nella documentazione.


2
Se stai creando un dizionario di conteggi, dovresti usare Counter (supponendo Python 2.7). E ho usato defaultdict(lambda: 0)invece defaultdict(int)perché penso che sia più chiaro cosa sta succedendo; il lettore non ha bisogno di sapere che ricevi 0se chiami int()senza argomenti. YMMV.
Chris B.

9

Controlla se una determinata chiave esiste già in un dizionario

Per avere un'idea di come fare, controlliamo prima quali metodi possiamo chiamare sul dizionario. Ecco i metodi:

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

Il metodo brutale per verificare se la chiave esiste già potrebbe essere il get()metodo:

d.get("key")

Gli altri due metodi interessantiitems() e keys()sembra troppo lavoro. Quindi esaminiamo se get()è il metodo giusto per noi. Abbiamo il nostro dict d:

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

La stampa mostra che la chiave che non abbiamo sarà restituita None:

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

Si può utilizzare che per avere le informazioni se la chiave è presente o no. Ma considera questo se creiamo un dict con un singolo key:None:

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

Condurre questo get()metodo non è affidabile nel caso in cui alcuni valori possano esserlo None. Questa storia dovrebbe avere un finale più felice. Se utilizziamo il incomparatore:

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

Otteniamo i risultati corretti. Possiamo esaminare il codice byte Python:

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

Ciò dimostra che l' inoperatore di confronto non è solo più affidabile ma anche più veloce di get().


.get()può avere un secondo argomento per defaultvalore, che potrebbe essere usato per gestire il problema dove key:None. esempio: d.get("key", False)
Alex

.get()è il modo più veloce. Un'altra opzione è quella di assegnare in un blocco try/except
HCLivess il

7

Il dizionario Python ha il metodo chiamato __contains__. Questo metodo restituirà True se il dizionario ha la chiave else restituisce 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.

2
È una cattiva pratica chiamare __contains__direttamente. Il modo corretto di farlo è usare l' inoperatore, che è quello containment checkche invoca la __contains__funzione.
user1767754

@ user1767754 Sto usando foo = x['foo'] if x.__contains__('foo') else 'bar'. Qualche idea su come potrebbe utilizzare l' inoperatore come parte di questa espressione?
donrondadon,

1
foo = x['foo'] if 'foo' in x else 'bar'
Ray Wu,

5

Condivisione di un altro modo per verificare l'esistenza di una chiave utilizzando operatori booleani.

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

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

Questo ritorna

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

Spiegazione

In primo luogo si deve sapere che in Python, 0, None, o gli oggetti con lunghezza zero restituiscono False. Tutto il resto valuta True. Le operazioni booleane vengono valutate da sinistra a destra e restituiscono l'operando non True o False.

Vediamo un esempio:

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

Poiché viene 'Some string'valutato True, il resto ornon viene valutato e non viene generato alcun errore di divisione per zero.

Ma se cambiamo l'ordine 1/0viene valutato prima e solleva un'eccezione:

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

Possiamo usarlo come modello per verificare se esiste una chiave.

(k in d and 'blah')

fa lo stesso di

if k in d:
    'blah'
else:
    False

Questo restituisce già il risultato corretto se la chiave esiste, ma vogliamo che stampi 'boo' quando non lo è. Quindi, prendiamo il risultato e orlo facciamo con'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

1

Puoi usare forloop per scorrere il dizionario e ottenere il nome della chiave che vuoi trovare nel dizionario, dopodiché controlla se esiste o meno usando la ifcondizione:

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')

controllare il codice perché l'output per questo è it is existenot exist
system123456

perché usare un dizionario se questo è per eseguire una ricerca lineare?
Jean-François Fabre
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.