Python! = operación vs "no es"
En un comentario sobre esta pregunta , vi una declaración que recomendaba usar
result is not None
vs
result != None
¿Cuál es la diferencia? ¿Y por qué podría recomendarse uno sobre el otro?
==
es una prueba de igualdad . Comprueba si el lado derecho y el lado izquierdo son objetos iguales (según sus métodos __eq__
o __cmp__
).
is
Es 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 is
operación.
Usas is
(y is not
) para singletons, como None
, donde no te importan los objetos que podrías querer fingir ser None
o donde quieres protegerte contra objetos que se rompan al compararlos con None
.
Primero, déjame repasar algunos términos. Si solo desea que se responda su pregunta, desplácese hacia abajo hasta "Responder a su pregunta".
Definiciones
Identidad del objeto : cuando crea un objeto, puede asignarlo a una variable. Luego también puedes 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 dismiss
todos se refieren al mismo objeto en la memoria. Solo creó un Button
objeto y las tres variables se refieren a este objeto. Decimos que cancel
, close
y dismiss
todos se refieren a objetos idénticos ; es decir, se refieren a un solo objeto.
Igualdad de objetos : cuando comparas dos objetos, normalmente no te importa que se refiera exactamente al mismo objeto en la memoria. Con la igualdad de objetos, puedes definir tus 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 a
para que pueda utilizar 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 otras nuevas en la memoria.
Aquí tengo dos cadenas Unicode a
y b
. Tienen exactamente el mismo contenido, pero no son el mismo objeto en la memoria. Sin embargo, cuando los comparamos, queremos que sean iguales. 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 unicode
definitivamente 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 de 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 a
y 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 sean iguales. 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
, utilice siempre is not
. Ninguno es un singleton en Python: solo hay una instancia en la memoria.
Al comparar la identidad , esto se puede realizar muy rápidamente. Python comprueba si el objeto al que te refieres tiene la misma dirección de memoria que el objeto global Ninguno: 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 es así, examina cada superclase en busca de un __eq__
método. Si encuentra uno, Python lo llama. Esto es especialmente malo si el __eq__
método es lento y no regresa inmediatamente cuando nota que el otro objeto es None
.
¿ No lo implementaste __eq__
? Entonces Python probablemente encontrará el __eq__
método object
y lo usará en su lugar, lo que de todos modos solo verifica la identidad del objeto.
Al comparar la mayoría de las otras cosas en Python, usará !=
.
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)
None
es un singleton y, por lo tanto, la comparación de identidad siempre funcionará, mientras que un objeto puede falsificar la comparación de igualdad mediante .__eq__()
.
>>> () es () Verdadero >>> 1 es 1 Verdadero >>> (1,) == (1,) Verdadero >>> (1,) es (1,) FALSO >>> a = (1,) >>> b = a >>> a es b Verdadero
Algunos objetos son singletons y, por lo tanto, is
con ellos es equivalente a ==
. La mayoría no lo es.