C'è una differenza tra ==
e is
in Python?
Sì, hanno una differenza molto importante.
==
: controlla l'uguaglianza - la semantica è che gli oggetti equivalenti (che non sono necessariamente lo stesso oggetto) testeranno come uguali. Come dice la documentazione :
Gli operatori <,>, ==,> =, <= e! = Confrontano i valori di due oggetti.
is
: controlla l'identità - la semantica è che l'oggetto (come tenuto in memoria) è l'oggetto. Ancora una volta, la documentazione dice :
Gli operatori is
e is not
test per l'identità dell'oggetto: x is y
è vero se e solo se x
e y
sono lo stesso oggetto. L'identità dell'oggetto viene determinata utilizzando la id()
funzione. x is not y
produce il valore della verità inversa.
Pertanto, il controllo dell'identità è uguale al controllo dell'uguaglianza degli ID degli oggetti. Questo è,
a is b
equivale a:
id(a) == id(b)
dove id
è la funzione incorporata che restituisce un numero intero che "è garantito essere unico tra oggetti esistenti simultaneamente" (vedi help(id)
) e dove a
e b
sono oggetti arbitrari.
Altre istruzioni per l'uso
Dovresti usare questi confronti per la loro semantica. Utilizzare is
per verificare l'identità e ==
per verificare l'uguaglianza.
Quindi, in generale, usiamo is
per verificare l'identità. Questo è di solito utile quando stiamo verificando un oggetto che dovrebbe esistere solo una volta in memoria, indicato come "singleton" nella documentazione.
I casi d'uso per is
includono:
None
- valori enum (quando si usano Enums dal modulo enum)
- di solito moduli
- di solito oggetti di classe risultanti dalle definizioni di classe
- di solito oggetti funzione derivanti dalle definizioni delle funzioni
- qualsiasi altra cosa che dovrebbe esistere solo una volta in memoria (tutti i singoli, in generale)
- un oggetto specifico che si desidera per identità
I casi d'uso usuali per ==
includono:
- numeri, compresi numeri interi
- stringhe
- liste
- imposta
- dizionari
- oggetti mutabili personalizzati
- altri oggetti immutabili incorporati, nella maggior parte dei casi
Il caso di uso generale, ancora una volta, per ==
, è l'oggetto che si desidera non può essere lo stesso oggetto, invece può essere un equivalente uno
PEP 8 direzioni
PEP 8, la guida ufficiale dello stile Python per la libreria standard menziona anche due casi d'uso peris
:
I confronti con i singleton come None
dovrebbero sempre essere fatti con is
o
is not
, mai, gli operatori di uguaglianza.
Inoltre, if x
fai attenzione a scrivere quando intendi davvero if x is not None
, ad esempio quando si verifica se una variabile o un argomento predefinito None
è impostato su un altro valore. L'altro valore potrebbe avere un tipo (come un contenitore) che potrebbe essere falso in un contesto booleano!
Inferenza dell'uguaglianza dall'identità
Se is
è vero, l'uguaglianza di solito può essere dedotta - logicamente, se un oggetto è se stesso, allora dovrebbe essere testato come equivalente a se stesso.
Nella maggior parte dei casi questa logica è vera, ma si basa sull'implementazione del __eq__
metodo speciale. Come dicono i documenti ,
Il comportamento predefinito per il confronto di uguaglianza ( ==
e !=
) si basa sull'identità degli oggetti. Quindi, il confronto di uguaglianza di istanze con la stessa identità provoca uguaglianza e il confronto di uguaglianza di istanze con identità diverse provoca disuguaglianza. Una motivazione per questo comportamento predefinito è il desiderio che tutti gli oggetti siano riflessi (ovvero x è y implica x == y).
e nell'interesse della coerenza, raccomanda:
Il confronto di uguaglianza dovrebbe essere riflessivo. In altre parole, gli oggetti identici dovrebbero confrontare uguale:
x is y
implica x == y
Possiamo vedere che questo è il comportamento predefinito per gli oggetti personalizzati:
>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)
Anche il contrappunto è di solito vero: se qualcosa risulta non uguale, di solito si può dedurre che non sono lo stesso oggetto.
Poiché i test per l'uguaglianza possono essere personalizzati, questa inferenza non è sempre valida per tutti i tipi.
Un'eccezione
Un'eccezione notevole è nan
- si verifica sempre come non uguale a se stesso:
>>> nan = float('nan')
>>> nan
nan
>>> nan is nan
True
>>> nan == nan # !!!!!
False
Il controllo dell'identità può essere molto più rapido del controllo dell'uguaglianza (che potrebbe richiedere un controllo ricorsivo dei membri).
Ma non può essere sostituito per l'uguaglianza in cui è possibile trovare più di un oggetto come equivalente.
Si noti che il confronto dell'uguaglianza di elenchi e tuple presuppone che l'identità degli oggetti sia uguale (poiché si tratta di un controllo rapido). Ciò può creare contraddizioni se la logica è incoerente, come per nan
:
>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True
Un racconto cautelativo:
La domanda sta tentando di utilizzare is
per confrontare numeri interi. Non si deve supporre che un'istanza di un numero intero sia la stessa istanza di una ottenuta da un altro riferimento. Questa storia spiega perché.
Un commentatore aveva un codice che si basava sul fatto che piccoli numeri interi (da -5 a 256 inclusi) sono singleton in Python, invece di verificare l'uguaglianza.
Wow, questo può portare ad alcuni bug insidiosi. Avevo del codice che controllava se a è b, che funzionava come volevo perché aeb sono in genere numeri piccoli. Il bug si è verificato solo oggi, dopo sei mesi di produzione, perché aeb erano finalmente abbastanza grandi da non poter essere memorizzati nella cache. - gwg
Ha funzionato nello sviluppo. Potrebbe aver superato alcuni unittest.
E ha funzionato in produzione - fino a quando il codice non ha verificato un numero intero maggiore di 256, a quel punto ha fallito nella produzione.
Si tratta di un errore di produzione che potrebbe essere stato rilevato durante la revisione del codice o eventualmente con un controllo di stile.
Vorrei sottolineare: non utilizzare is
per confrontare numeri interi.
echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - foo
Uscita:False True False
.