Python! = Operazione vs "non è"


250

In un commento su questa domanda , ho visto una dichiarazione che raccomandava di usare

result is not None

vs

result != None

Mi chiedevo quale fosse la differenza e perché uno potrebbe essere raccomandato rispetto all'altro?



1
Hmm. Mentre la risposta ad entrambe le domande è lo stesso concetto, penso che i voti positivi e le risposte dettagliate qui contribuiscano indipendentemente al concetto di test di identità e uguaglianza.
viksit,

Risposte:


301

==è un test di uguaglianza . Verifica se il lato destro e il lato sinistro sono oggetti uguali (secondo i metodi __eq__o __cmp__).

isè un test di identità . Verifica se il lato destro e il lato sinistro sono lo stesso oggetto. Non viene eseguito alcun metodo, gli oggetti non possono influenzare l' isoperazione.

Usi is(e is not) per i singleton, ad esempio None, dove non ti importa degli oggetti che potrebbero voler fingere di essere Noneo dove vuoi proteggere dagli oggetti che si rompono quando vengono confrontati None.


3
Grazie per la risposta: potresti approfondire le situazioni in cui un oggetto può rompersi, confrontato con None?
viksit,

3
@viksit. Noneha pochi metodi e quasi nessun attributo. Se il __eq__test prevede un metodo o un attributo, potrebbe interrompersi. def __eq__( self, other ): return self.size == other.size. Ad esempio, si romperà se othersuccede None.
S. Lott,

36
Il mio modo preferito per capirlo è: quello di Python isè come quello di Java ==. Python ==è come Java .equals(). Naturalmente questo aiuta solo se conosci Java.
MatrixFrog,

4
@MatrixFrog: in PHP o JavaScript si direbbe che isè simile ===(molto uguale) e viceversa is notè simile !==(non esattamente uguale).
Orwellophile,

3
È is notun singolo operatore o sta semplicemente negando il risultato di iscome internamente not foo is bar?
Asad Moosvi,

150

Prima di tutto, lasciami esaminare alcuni termini. Se desideri solo rispondere alla tua domanda, scorri verso il basso fino a "Rispondi alla tua domanda".

definizioni

Identità oggetto : quando si crea un oggetto, è possibile assegnarlo a una variabile. È quindi possibile anche assegnarlo a un'altra variabile. E un altro.

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

In questo caso, cancel, close, e dismisssi riferiscono tutti allo stesso oggetto in memoria. Hai creato solo un Buttonoggetto e tutte e tre le variabili si riferiscono a questo oggetto. Diciamo che cancel, closee dismisstutti si riferiscono a oggetti identici ; cioè, si riferiscono a un singolo oggetto.

Uguaglianza degli oggetti : quando si confrontano due oggetti, di solito non ti interessa che si riferisca esattamente allo stesso oggetto in memoria. Con l'uguaglianza degli oggetti, è possibile definire le proprie regole per il confronto tra due oggetti. Quando scrivi if a == b:, essenzialmente dici if a.__eq__(b):. Ciò consente di definire un __eq__metodo in amodo da poter utilizzare la propria logica di confronto.

Razionale per confronti di uguaglianza

Motivazione: Due oggetti hanno gli stessi dati esatti, ma non sono identici. (Non sono lo stesso oggetto in memoria.) Esempio: stringhe

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

Nota: utilizzo qui stringhe unicode perché Python è abbastanza intelligente da riutilizzare stringhe regolari senza crearne di nuove in memoria.

Qui, ho due stringhe unicode ae b. Hanno lo stesso contenuto esatto, ma non sono lo stesso oggetto in memoria. Tuttavia, quando li confrontiamo, vogliamo che siano uguali. Quello che sta succedendo qui è che l'oggetto unicode ha implementato il __eq__metodo.

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

Nota: __eq__on unicodeè sicuramente implementato in modo più efficiente di così.

Motivazione: Due oggetti hanno dati diversi, ma sono considerati lo stesso oggetto se alcuni dati chiave sono uguali. Esempio: la maggior parte dei tipi di dati del modello

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

Qui, ho due monitor Dell ae b. Hanno la stessa marca e modello. Tuttavia, non hanno gli stessi dati né lo stesso oggetto in memoria. Tuttavia, quando li confrontiamo, vogliamo che siano uguali. Quello che sta succedendo qui è che l'oggetto Monitor ha implementato il __eq__metodo.

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

Rispondere alla tua domanda

In confronto a None, utilizzare sempre is not. Nessuno è un singleton in Python - ne esiste solo un'istanza in memoria.

Confrontando l' identità , questo può essere eseguito molto rapidamente. Python verifica se l'oggetto a cui ti riferisci ha lo stesso indirizzo di memoria dell'oggetto Nessuno globale, un confronto molto, molto veloce di due numeri.

Confrontando l' uguaglianza , Python deve cercare se il tuo oggetto ha un __eq__metodo. In caso contrario, esamina ogni superclasse alla ricerca di un __eq__metodo. Se ne trova uno, Python lo chiama. Ciò è particolarmente negativo se il __eq__metodo è lento e non ritorna immediatamente quando nota che l'altro oggetto è None.

Non hai implementato __eq__? Quindi Python probabilmente troverà il __eq__metodo attivo objecte lo userà invece, il che controlla comunque l'identità dell'oggetto.

Quando confronterai la maggior parte delle altre cose in Python, utilizzerai !=.


42

Considera quanto segue:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)

1
Questo è un esempio molto utile e semplice. Grazie.
msarafzadeh,

18

Noneè un singleton, quindi il confronto di identità funzionerà sempre, mentre un oggetto può falsificare il confronto di uguaglianza tramite .__eq__().


Ah interessante! In quali situazioni si potrebbe voler fingere il confronto di uguaglianza tra l'altro? Immagino che questo abbia delle ripercussioni sulla sicurezza in qualche modo.
viksit,

1
Non si tratta di falsificare l'uguaglianza, si tratta di implementare l' uguaglianza. Ci sono molte ragioni per voler definire come un oggetto si confronta con un altro.
Thomas Wouters,

1
Direi che sono più implicazioni di confusione che implicazioni di sicurezza.
Greg Hewgill,

2
Non ho trovato un motivo per simulare l'uguaglianza None, ma un comportamento scorretto in merito Nonepotrebbe verificarsi come effetto collaterale dell'implementazione dell'uguaglianza contro altri tipi. Non sono tante implicazioni per la sicurezza ma solo implicazioni sulla correttezza.
Ignacio Vazquez-Abrams,

Ah in quel modo, vedo. Grazie per il chiarimento.
viksit,

10
>>> () è ()
Vero
>>> 1 è 1
Vero
>>> (1,) == (1,)
Vero
>>> (1,) è (1,)
falso
>>> a = (1,)
>>> b = a
>>> a è b
Vero

Alcuni oggetti sono singoli, e quindi iscon essi è equivalente ==. La maggior parte no.


4
La maggior parte di questi funziona solo per coincidenza / dettagli di implementazione. ()e 1non sono intrinsecamente singoli.
Mike Graham,

1
Nell'implementazione di CPython, i piccoli numeri interi ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) e le tuple vuote sono singoli. In effetti non è documentato né garantito, ma è improbabile che cambi.
effimero

3
È come viene implementato, ma non è significativo o utile o educativo.
Mike Graham,

1
E in particolare, CPython non è l'unica implementazione di Python. Affidarmi a comportamenti che possono variare a seconda delle implementazioni di Python sembrerebbe essere generalmente una cattiva idea per me.
io_e il
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.