Este é um ótimo exemplo de por que os __dunder__métodos não devem ser usados diretamente, pois geralmente não são substituições apropriadas para seus operadores equivalentes; em ==vez disso, você deve usar o operador para comparações de igualdade ou, nesse caso especial, ao verificar None, use is(pule para a parte inferior da resposta para obter mais informações).
Você fez
None.__eq__('a')
# NotImplemented
Que retorna NotImplementeddesde que os tipos sendo comparados são diferentes. Considere outro exemplo em que dois objetos com tipos diferentes estão sendo comparados dessa maneira, como 1e 'a'. Fazer (1).__eq__('a')também não está correto e retornará NotImplemented. A maneira correta de comparar esses dois valores para a igualdade seria
1 == 'a'
# False
O que acontece aqui é
- Primeiro,
(1).__eq__('a')é tentado, que retorna NotImplemented. Isso indica que a operação não é suportada, portanto
'a'.__eq__(1)é chamado, que também retorna o mesmo NotImplemented. Assim,
- Os objetos são tratados como se não fossem os mesmos e
Falsesão retornados.
Aqui está um ótimo MCVE usando algumas classes personalizadas para ilustrar como isso acontece:
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
Obviamente, isso não explica por que a operação retorna verdadeira. Isso ocorre porque, NotImplementedna verdade, é um valor verdadeiro:
bool(None.__eq__("a"))
# True
Igual a,
bool(NotImplemented)
# True
Para obter mais informações sobre quais valores são considerados verdadeiros e falsos, consulte a seção de documentos em Teste do valor da verdade , bem como esta resposta . Vale a pena notar aqui que NotImplementedé verdade, mas teria sido uma história diferente se a classe definisse um método __bool__ou __len__que retornasse Falseou 0respectivamente.
Se você deseja o equivalente funcional do ==operador, use operator.eq:
import operator
operator.eq(1, 'a')
# False
No entanto, conforme mencionado anteriormente, para este cenário específico , em que você está procurando None, use is:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
O equivalente funcional disso está usando operator.is_:
operator.is_(var2, None)
# True
Noneé um objeto especial e apenas existe uma versão na memória a qualquer momento. IOW, é o único singleton da NoneTypeclasse (mas o mesmo objeto pode ter qualquer número de referências). As diretrizes do PEP8 explicitam isso:
As comparações com singletons como Nonesempre devem ser feitas com isou
is not, nunca com os operadores de igualdade.
Em resumo, para singletons como None, uma verificação de referência isé mais apropriada, embora ambas ==e isfuncionem perfeitamente.