Python! = Operación vs “no es”

En un comentario sobre esta pregunta , vi una statement que recomendaba usar

result is not None 

vs

 result != None 

Me preguntaba cuál es la diferencia y por qué podría recomendarse una sobre la otra.

== 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 una prueba de identidad . Comprueba si el lado derecho y el lado izquierdo son el mismo objeto. No se realizan llamadas de método, los objetos no pueden influir en la operación.

Usted usa is (y is not ) para singletons, como None , donde no le importan los objetos que podrían querer ser None o donde quiere protegerse contra los objetos que se rompen cuando se los compara con None .

Primero, déjame repasar algunos términos. Si solo quiere que se responda su pregunta, desplácese hacia abajo hasta “Responder a su pregunta”.

Definiciones

Identidad del objeto : cuando creas un objeto, puedes asignarlo a una variable. 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 refieren al mismo objeto en la memoria. Solo creó un objeto Button , y las tres variables se refieren a este objeto. Decimos que cancel , close y dismiss refiere a objetos idénticos ; es decir, se refieren a un solo objeto.

Igualdad de objetos : cuando comparas dos objetos, generalmente no te importa que se refiera exactamente al mismo objeto en la memoria. Con la igualdad de objetos, puede definir sus propias reglas para la comparación de dos objetos. Cuando escribe if a == b: básicamente está diciendo if a.__eq__(b): Esto le permite definir un método __eq__ en a forma 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 de Unicode, 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 ocurre aquí es que el objeto Unicode ha implementado el método __eq__ .

 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__ en 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 iguales. 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, b . Tienen la misma marca y modelo. Sin embargo, ni 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 método __eq__ .

 class Monitor(object): # ... def __eq__(self, other): return self.make == other.make and self.model == other.model 

Respondiendo tu pregunta

Cuando se compara con None , usar siempre is not . Ninguno es un singleton en Python, solo hay una instancia de él en la memoria.

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

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

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

Cuando compares la mayoría de las otras cosas en Python, estarás usando != .

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, 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__() .

 >>> () 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 is con ellos es equivalente a == . La mayoría no lo son.