Este es un gran ejemplo de por qué los __dunder__métodos no deben usarse directamente, ya que a menudo no son reemplazos apropiados para sus operadores equivalentes; en su lugar, debe usar el ==operador para las comparaciones de igualdad, o en este caso especial, al verificar None, usar is(pase al final de la respuesta para obtener más información).
Has hecho
None.__eq__('a')
# NotImplemented
Que regresa NotImplementedya que los tipos que se comparan son diferentes. Considere otro ejemplo en el que dos objetos con diferentes tipos se comparan de esta manera, como 1y 'a'. Hacer (1).__eq__('a')tampoco es correcto y volverá NotImplemented. La forma correcta de comparar estos dos valores para la igualdad sería
1 == 'a'
# False
Lo que pasa aquí es
- Primero,
(1).__eq__('a')se prueba, que vuelve NotImplemented. Esto indica que la operación no es compatible, por lo que
'a'.__eq__(1)se llama, que también devuelve lo mismo NotImplemented. Entonces,
- Los objetos se tratan como si no fueran lo mismo y
Falsese devuelven.
Aquí hay un pequeño MCVE que usa algunas clases personalizadas para ilustrar cómo sucede esto:
class A:
def __eq__(self, other):
print('A.__eq__')
return NotImplemented
class B:
def __eq__(self, other):
print('B.__eq__')
return NotImplemented
class C:
def __eq__(self, other):
print('C.__eq__')
return True
a = A()
b = B()
c = C()
print(a == b)
# A.__eq__
# B.__eq__
# False
print(a == c)
# A.__eq__
# C.__eq__
# True
print(c == a)
# C.__eq__
# True
Por supuesto, eso no explica por qué la operación devuelve verdadero. Esto se debe a NotImplementedque en realidad es un valor verdadero:
bool(None.__eq__("a"))
# True
Igual que,
bool(NotImplemented)
# True
Para obtener más información sobre qué valores se consideran verdadero y falso, consulte la sección de documentos sobre Pruebas de valor de verdad , así como esta respuesta . Vale la pena señalar aquí que NotImplementedes verdad, pero habría sido una historia diferente si la clase hubiera definido un método __bool__o un __len__método que regresó Falseo 0respectivamente.
Si desea el equivalente funcional del ==operador, use operator.eq:
import operator
operator.eq(1, 'a')
# False
Sin embargo, como se mencionó anteriormente, para este escenario específico , donde está buscando None, use is:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
El equivalente funcional de esto es usar operator.is_:
operator.is_(var2, None)
# True
Nonees un objeto especial, y solo existe 1 versión en la memoria en cualquier momento. IOW, es el único singleton de la NoneTypeclase (pero el mismo objeto puede tener cualquier número de referencias). Las pautas PEP8 hacen esto explícito:
Las comparaciones con singletons como Nonesiempre deben hacerse con iso
is not, nunca con los operadores de igualdad.
En resumen, para singletons como None, una verificación de referencia con ises más apropiada, aunque ambas ==y isfuncionarán bien.