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 NotImplemented
desde 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 1
e '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
False
sã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, NotImplemented
na 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 False
ou 0
respectivamente.
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 NoneType
classe (mas o mesmo objeto pode ter qualquer número de referências). As diretrizes do PEP8 explicitam isso:
As comparações com singletons como None
sempre devem ser feitas com is
ou
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 is
funcionem perfeitamente.