data - getattr python
¿Por qué `if None.__ eq__(" a ")` parece evaluarse como Verdadero(pero no del todo)? (4)
¿Por qué?
Devuelve un
NotImplemented
, sí:
>>> None.__eq__(''a'')
NotImplemented
>>>
Pero si nos fijamos en esto:
>>> bool(NotImplemented)
True
>>>
NotImplemented
es en realidad un valor veraz, por eso es que devuelve
b
, todo lo que es
True
pasará, todo lo que sea
False
no lo hará.
¿Cómo resolverlo?
Debe comprobar si es
True
, así que tenga más sospechas, como ve:
>>> NotImplemented == True
False
>>>
Así que harías:
>>> if None.__eq__(''a'') == True:
print(''b'')
>>>
Y como ves, no devolvería nada.
Si ejecuta la siguiente declaración en Python 3.7, (de mis pruebas) se imprimirá
b
:
if None.__eq__("a"):
print("b")
Sin embargo,
None.__eq__("a")
evalúa como No
NotImplemented
.
Naturalmente,
"a".__eq__("a")
evalúa como
True
, y
"b".__eq__("a")
evalúa como
False
.
Inicialmente descubrí esto al probar el valor de retorno de una función, pero no devolví nada en el segundo caso, por lo que la función devolvió
None
.
¿Que está pasando aqui?
Como ya ha calculado,
None.__eq__("a")
evalúa como No
NotImplemented
sin embargo, si intenta algo como
if NotImplemented:
print("Yes")
else:
print("No")
el resultado es
sí
esto significa que el valor de verdad de
NotImplemented
true
Por lo tanto el resultado de la pregunta es obvio:
None.__eq__(something)
produce No
NotImplemented
Y
bool(NotImplemented)
evalúa a True
Entonces,
if None.__eq__("a")
siempre es Verdadero
El resultado que está viendo es causado por el hecho de que
None.__eq__("a") # evaluates to NotImplemented
evalúa a
NotImplemented
, y el valor de verdad de
NotImplemented
está documentado como
True
:
https://docs.python.org/3/library/constants.html
Valor especial que debe ser devuelto por los métodos especiales binarios (por ejemplo,
__eq__()
,__lt__()
,__add__()
,__rsub__()
, etc.) para indicar que la operación no está implementada con respecto al otro tipo; puede ser devuelto por los métodos especiales binarios en el lugar (por ejemplo,__imul__()
,__iand__()
, etc.) para el mismo propósito. Su valor de verdad es verdadero.
Si llama al
__eq()__
manualmente en lugar de usar simplemente
==
, debe estar preparado para lidiar con la posibilidad de que pueda devolver
NotImplemented
y que su valor verdadero sea verdadero.
Este es un gran ejemplo de por qué los métodos
__dunder__
no deben usarse directamente, ya que a menudo no son reemplazos apropiados para sus operadores equivalentes;
debe utilizar el operador
==
lugar de comparaciones de igualdad, o en este caso especial, al marcar
None
, el uso
is
(pase a la parte inferior de la respuesta para obtener más información).
Has hecho
None.__eq__(''a'')
# NotImplemented
Que devuelve No
NotImplemented
ya que los tipos que se comparan son diferentes.
Considere otro ejemplo en el que dos objetos con tipos diferentes se comparan de esta manera, como
1
y
''a''
.
Hacer
(1).__eq__(''a'')
tampoco es correcto, y devolverá
NotImplemented
.
La manera correcta de comparar estos dos valores para la igualdad sería
1 == ''a''
# False
Lo que pasa aqui es
-
Primero, se prueba
(1).__eq__(''a'')
, que devuelveNotImplemented
. Esto indica que la operación no es compatible, por lo que -
Se llama a
''a''.__eq__(1)
, que también devuelve el mismoNotImplemented
. Asi que, -
Los objetos se tratan como si no fueran los mismos y se devuelve
False
.
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 se vuelve verdadera.
Esto se debe a que
NotImplemented
es en realidad un valor verdadero:
bool(None.__eq__("a"))
# True
Igual que,
bool(NotImplemented)
# True
Para obtener más información sobre qué valores se consideran verdaderos y falsos, consulte la sección de documentos sobre
Pruebas del valor de verdad
, así como
esta respuesta
.
Vale la pena señalar aquí que
NotImplemented
es veraz, pero habría sido una historia diferente si la clase hubiera definido un método
__bool__
o
__len__
que devolviera
False
o
0
respectivamente.
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á comprobando que
None
, el uso
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
None
es un objeto especial, y solo una versión existe en la memoria en cualquier momento.
IOW, es el único singleton de la clase
NoneType
(pero el mismo objeto puede tener cualquier número de referencias).
Las
pautas de PEP8
hacen esto explícito:
Las comparaciones con singletons como
None
deben hacerse siempre con ois not
, nunca con los operadores de igualdad.
En resumen, para singletons como
None
, una verificación de referencia es más apropiada, aunque ambas
==
y funcionarán bien.