Python! = Operación vs "no es"

250

En un comentario sobre esta pregunta , vi una declaración que recomendaba usar

result is not None

vs

result != None

Me preguntaba cuál es la diferencia y por qué uno podría recomendarse sobre el otro.

viksit
fuente
1
Hmm Si bien la respuesta a ambas preguntas es el mismo concepto, creo que los votos a favor y las respuestas detalladas aquí contribuyen independientemente al concepto de prueba de identidad e igualdad.
viksit

Respuestas:

301

==Es una prueba de igualdad . Se comprueba si el lado derecho y el lado izquierdo son iguales objetos (según su __eq__o __cmp__métodos.)

isEs una prueba de identidad . Comprueba si el lado derecho y el lado izquierdo son el mismo objeto. No se realizan llamadas a métodos, los objetos no pueden influir en la isoperación.

Utiliza is(y is not) para los singletons, por ejemplo None, donde no le importan los objetos que podrían pretender ser Noneo donde desea protegerse contra los objetos que se rompen cuando se los compara None.

Thomas Wouters
fuente
3
Gracias por la respuesta: ¿podría explicar las situaciones en que un objeto puede romperse, comparándolo con Ninguno?
viksit
3
@viksit. Nonetiene pocos métodos y casi no tiene atributos. Si su __eq__prueba esperaba un método o atributo, podría romperse. def __eq__( self, other ): return self.size == other.size. Por ejemplo, se romperá si otherresulta ser None.
S.Lott
36
Mi forma favorita de comprender esto es: Python ises como el de Java ==. Python ==es como el de Java .equals(). Por supuesto, esto solo ayuda si conoces Java.
MatrixFrog
44
@MatrixFrog: en PHP o JavaScript diríamos que ises como ===(muy igual), y por el contrario is notes como !==(no exactamente igual).
Orwellophile
3
¿Es is notun solo operador o simplemente está negando el resultado de ser isinterno not foo is bar?
Asad Moosvi
150

Primero, déjenme repasar algunos términos. Si solo desea que su pregunta sea respondida, desplácese hacia abajo hasta "Responder su pregunta".

Definiciones

Identidad del objeto : cuando crea un objeto, puede asignarlo a una variable. Entonces también puede asignarlo a otra variable. Y otro.

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

En este caso, cancel, close, y dismisstodos se refieren al mismo objeto en la memoria. Solo creó un Buttonobjeto, y las tres variables se refieren a este objeto. Decimos eso cancel, closey dismisstodos se refieren a objetos idénticos ; es decir, se refieren a un solo objeto.

Igualdad de objeto : cuando compara dos objetos, generalmente no le importa que se refiera exactamente al mismo objeto en la memoria. Con la igualdad de objetos, puede definir sus propias reglas sobre cómo se comparan dos objetos. Cuando escribes if a == b:, esencialmente estás diciendo if a.__eq__(b):. Esto le permite definir un __eq__método apara que pueda usar su propia lógica de comparación.

Justificación de las comparaciones de igualdad

Justificación: Dos objetos tienen exactamente los mismos datos, pero no son idénticos. (No son el mismo objeto en la memoria). Ejemplo: cadenas

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

Nota: Aquí uso cadenas Unicode porque Python es lo suficientemente inteligente como para reutilizar cadenas normales sin crear nuevas en la memoria.

Aquí, tengo dos cadenas unicode, ay b. Tienen exactamente el mismo contenido, pero no son el mismo objeto en la memoria. Sin embargo, cuando los comparamos, queremos que se comparen igual. Lo que sucede aquí es que el objeto Unicode ha implementado el __eq__método.

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 unicodedefinitivamente se implementa de manera más eficiente que esto.

Justificación: Dos objetos tienen datos diferentes, pero se consideran el mismo objeto si algunos datos clave son los mismos. Ejemplo: la mayoría de los tipos de datos del modelo

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

Aquí tengo dos monitores Dell ay b. Tienen la misma marca y modelo. Sin embargo, no tienen los mismos datos ni son el mismo objeto en la memoria. Sin embargo, cuando los comparamos, queremos que se comparen igual. Lo que sucede aquí es que el objeto Monitor implementó el __eq__método.

class Monitor(object):
    # ...

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

Respondiendo tu pregunta

Al comparar con None, siempre use is not. Ninguno es un singleton en Python; solo hay una instancia de este en la memoria.

Al comparar la identidad , esto se puede realizar muy rápidamente. Python comprueba si el objeto al que se refiere tiene la misma dirección de memoria que el objeto Ninguno global: una comparación muy, muy rápida de dos números.

Al comparar la igualdad , Python tiene que buscar si su objeto tiene un __eq__método. Si no lo hace, examina cada superclase buscando un __eq__método. Si encuentra uno, Python lo llama. Esto es especialmente malo si el __eq__método es lento y no regresa de inmediato cuando se da cuenta de que el otro objeto lo es None.

¿No lo implementaste __eq__? Entonces, Python probablemente encontrará el __eq__método objecty lo usará en su lugar, que de todos modos solo verifica la identidad del objeto.

Al comparar la mayoría de las otras cosas en Python, lo usará !=.

Wesley
fuente
42

Considera lo siguiente:

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)
Alok Singhal
fuente
1
Este es un ejemplo muy útil y simple. Gracias.
msarafzadeh
18

Nonees un singleton, por lo tanto, la comparación de identidad siempre funcionará, mientras que un objeto puede falsificar la comparación de igualdad a través de .__eq__().

Ignacio Vazquez-Abrams
fuente
Ah interesante! ¿En qué situaciones podría uno querer fingir la comparación de igualdad por cierto? Supongo que esto tiene implicaciones de seguridad de alguna manera.
viksit
1
No se trata de fingir la igualdad, se trata de implementar la igualdad. Hay muchas razones para querer definir cómo se compara un objeto con otro.
Thomas Wouters
1
Diría que son más implicaciones de confusión que implicaciones de seguridad.
Greg Hewgill
2
No he encontrado una razón para fingir la igualdad None, pero un comportamiento incorrecto Nonepodría ocurrir como un efecto secundario de la implementación de la igualdad contra otros tipos. No son tanto las implicaciones de seguridad como las implicaciones de corrección.
Ignacio Vazquez-Abrams
Ah de esa manera, ya veo. Gracias por la aclaración.
viksit
10
>>> () es ()
Cierto
>>> 1 es 1
Cierto
>>> (1,) == (1,)
Cierto
>>> (1,) es (1,)
Falso
>>> a = (1,)
>>> b = a
>>> a es b
Cierto

Algunos objetos son singletons y, por lo tanto, iscon ellos es equivalente a ==. La mayoría no lo son.

efímero
fuente
44
La mayoría de estos solo funcionan por coincidencia / detalle de implementación. ()y 1no son inherentemente solteros.
Mike Graham
1
En la implementación de CPython, los enteros pequeños ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) y las tuplas vacías son singletons. De hecho, no está documentado ni garantizado, pero es poco probable que cambie.
Ephemient
3
Es cómo se implementa, pero no es significativo, útil o educativo.
Mike Graham
1
Y en particular, CPython no es la única implementación de Python. Confiar en el comportamiento que puede variar entre las implementaciones de Python en general parece ser una mala idea para mí.
yo_y el