2010-09-17 16 views
28

Eventuali duplicati:
'has_key()' or 'in'?determinare se una chiave è presente in un dizionario

ho un dizionario Python come:

mydict = {'name':'abc','city':'xyz','country','def'} 

voglio verificare se un la chiave è nel dizionario o no. Sono ansioso di sapere ciò che è più preferibile dai seguenti due casi e perché?

1> if mydict.has_key('name'): 
2> if 'name' in mydict: 
+5

BTW, 'dict 'è il nome di un tipo Python integrato, quindi è meglio evitare di usarlo come nome variabile negli script (anche se in senso stretto, è legale farlo). – martineau

+4

[i documenti sono abbastanza chiari] (http://docs.python.org/library/stdtypes.html#dict.has_key), no? – SilentGhost

+0

In Python 3, gli oggetti 'dict' non hanno più un metodo' has_key() ', quindi version-portability-wise, l'operatore' in' è migliore. – martineau

risposta

42
if 'name' in mydict: 

è la preferita, versione divinatorio. L'utilizzo di has_key() è sconsigliato e questo metodo has been removed in Python 3.

+2

Inoltre, '" nome "in dict' funzionerà con qualsiasi iterable e non solo dizionari. –

+2

E riguardo 'dict.get (chiave)'? Questo dovrebbe anche essere evitato? – user225312

+7

@PulpFiction: 'dict.get (chiave)' può essere utile quando tu (1) non vuoi un 'KeyError' nel caso in cui 'key' non sia in dict (2) vuoi usare un valore predefinito se ci non è 'chiave' (' dict.get (chiave, impostazione predefinita) '). Il punto 2 può essere fatto anche usando 'defaultdict'. –

13

In termini di bytecode, in salva una LOAD_ATTR e sostituisce un CALL_FUNCTION con un COMPARE_OP.

>>> dis.dis(indict) 
    2   0 LOAD_GLOBAL    0 (name) 
       3 LOAD_GLOBAL    1 (d) 
       6 COMPARE_OP    6 (in) 
       9 POP_TOP    


>>> dis.dis(haskey) 
    2   0 LOAD_GLOBAL    0 (d) 
       3 LOAD_ATTR    1 (haskey) 
       6 LOAD_GLOBAL    2 (name) 
       9 CALL_FUNCTION   1 
      12 POP_TOP    

I miei sentimenti sono che in è molto più leggibile ed è da preferire in tutti i casi che mi vengono in mente.

In termini di prestazioni, la tempistica riflette l'opcode

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "'foo' in d" 
10000000 loops, best of 3: 0.11 usec per loop 

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "d.has_key('foo')" 
    1000000 loops, best of 3: 0.205 usec per loop 

in è quasi due volte più veloce.

+1

Eventuali misure di velocità sono ovviamente problematiche, in genere non pertinenti, dipendenti dall'implementazione, potenzialmente dipendenti dalla versione e meno importanti dei problemi di deprecazione e stile. –

+2

@ Mike Graham, hai quasi ragione. Ho fatto caso peggiore lì dentro pensato, perché, IMO, è dove vuoi davvero sapere. Inoltre, penso che il tuo atteggiamento sia (anche se assolutamente corretto), un po 'più appropriato per un linguaggio come C, dove è veloce in entrambi i casi, a meno che tu non rovini davvero qualcosa. In Python si paga per ottenere il diritto in misura maggiore. Inoltre, gli sviluppatori core hanno un modo per mettere a punto "l'unico modo giusto" per fare qualcosa in modo che, di nuovo, le prestazioni siano un buon indicatore del buon stile in misura maggiore del normale in una lingua. – aaronasterling

7

La mia risposta è "nessuno dei due".

Credo che il modo più "Python" per fare le cose è NON controllare preventivamente se la chiave è in un dizionario e invece basta scrivere codice che presume che sia lì e prendere qualsiasi KeyErrors che si alza perché non lo era.

Questo è solitamente fatto con racchiudendo il codice in una clausola try...except ed è un linguaggio ben noto di solito espressa come "è più facile chiedere perdono che il permesso" o con l'acronimo EAFP, che sostanzialmente significa che è meglio provare qualcosa e catturare gli errori invece per assicurarsi che tutto sia OK prima di fare qualsiasi cosa. Perché convalidare ciò che non ha bisogno di essere convalidato quando puoi gestire le eccezioni con grazia invece di cercare di evitarli? Perché è spesso più leggibile e il codice tende ad essere più veloce se la probabilità è bassa che la chiave non sarà lì (o qualunque precondizione ci possa essere).

Naturalmente, questo non è appropriato in tutte le situazioni e non tutti sono d'accordo con la filosofia, quindi dovrai decidere tu stesso caso per caso. Non sorprendentemente l'opposto di questo è chiamato LBYL per "Look Before You Leap".

Come esempio banale considerazione:

if 'name' in dct: 
    value = dct['name'] * 3 
else: 
    logerror('"%s" not found in dictionary, using default' % name) 
    value = 42 

vs

try: 
    value = dct['name'] * 3 
except KeyError: 
    logerror('"%s" not found in dictionary, using default' % name) 
    value = 42 

Anche se nel caso è quasi esattamente la stessa quantità di codice, il secondo non spendere tempo a controllare prima ed è probabilmente un po 'più veloce a causa di esso (prova ... tranne il blocco non è completamente gratuito, quindi probabilmente non fa molta differenza qui).

In generale, i test in anticipo possono spesso essere molto più coinvolti e il risparmio ottenuto dal non farlo può essere significativo. Detto questo, if 'name' in dict: è migliore per i motivi indicati nelle altre risposte.

Se siete interessati al tema, questo message dal titolo "EAFP vs LBYL (era Re: Un po 'deluso finora)" dal Python archivio della mailing list probabilmente spiega la differenza tra i due è avvicinato meglio di me avere qui C'è anche una buona discussione sui due approcci nel libro Python in a Nutshell, 2 ° Ed di Alex Martelli nel capitolo sulle eccezioni intitolato "Error-Checking Strategies".

+1

Ci sono dati per supportare la dichiarazione "il guadagno derivante dal non farlo può essere significativo"? Come sviluppatore Java, sono abituato a pensare che le eccezioni siano costose e debbano essere per situazioni davvero eccezionali. La tua raccomandazione suona come "eccezione come goto". Puoi citare una fonte? – duffymo

+0

@duffymo. No, non posso citare una fonte. Ho fatto la dichiarazione perché di solito ci sono molti modi in cui qualcosa può andare storto, rispetto a un numero relativamente piccolo di giuste vie. Il controllo di tutti i modi sbagliati può coinvolgere molto codice (che spesso è anche noioso scrivere). Gestire le eccezioni può essere lento in alcune lingue e ho menzionato specificamente che questo potrebbe non essere un buon modo per andare se ci si aspetta che accadano di frequente. Inoltre, non sto sostenendo l'utilizzo di eccezioni come parte del normale o regolare flusso di controllo di un programma: dovrebbero essere utilizzate, come lei dice, per circostanze eccezionali. – martineau

+4

Le eccezioni in Python sono costose. Se si prevede che la chiave manchi più di qualche percento delle volte, il costo dell'eccezione probabilmente domina il runtime della funzione. –

18

Nella stessa vena della risposta di martineau, la soluzione migliore spesso non è controllare. Ad esempio, il codice

if x in d: 
    foo = d[x] 
else: 
    foo = bar 

è normalmente scritto

foo = d.get(x, bar) 

che è più breve e parla di quello che vuoi dire in modo più diretto.

Un altro caso comune è qualcosa di simile

if x not in d: 
    d[x] = [] 

d[x].append(foo) 

che può essere riscritta

d.setdefault(x, []).append(foo) 

o riscritta meglio ancora utilizzando un collections.defaultdict(list) per d e la scrittura

d[x].append(foo) 
+0

Sì , qualcosa che potresti chiamare "impostazioni predefinite intelligenti" (o anche "progettazione intelligente" ;-) – martineau

+3

Naw, questi sono metodi e tipi che Python si è evoluto nel tempo. ';)' –

+0

OMG, hai ragione! – martineau

Problemi correlati