Ceci est un excellent exemple de la raison pour laquelle les __dunder__
méthodes ne devraient pas être utilisées directement car elles ne sont souvent pas des remplacements appropriés pour leurs opérateurs équivalents; vous devriez utiliser l' ==
opérateur à la place pour les comparaisons d'égalité, ou dans ce cas particulier, lors de la vérification None
, utilisez is
(passez au bas de la réponse pour plus d'informations).
Tu as fait
None.__eq__('a')
# NotImplemented
Quels retours NotImplemented
puisque les types comparés sont différents. Prenons un autre exemple où deux objets de types différents sont comparés de cette manière, tels que 1
et 'a'
. Faire (1).__eq__('a')
n'est pas non plus correct et reviendra NotImplemented
. La bonne façon de comparer ces deux valeurs pour l'égalité serait
1 == 'a'
# False
Ce qui se passe ici est
- Tout d'abord,
(1).__eq__('a')
est essayé, qui revient NotImplemented
. Cela indique que l'opération n'est pas prise en charge, donc
'a'.__eq__(1)
est appelé, qui renvoie également le même NotImplemented
. Alors,
- Les objets sont traités comme s'ils n'étaient pas identiques et
False
sont renvoyés.
Voici un joli petit MCVE utilisant des classes personnalisées pour illustrer comment cela se produit:
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
Bien sûr, cela n'explique pas pourquoi l'opération retourne true. C'est parce que NotImplemented
c'est en fait une valeur de vérité:
bool(None.__eq__("a"))
# True
Pareil que,
bool(NotImplemented)
# True
Pour plus d'informations sur les valeurs considérées comme véridiques et fausses, consultez la section de documentation sur le test de la valeur de vérité , ainsi que cette réponse . Il est intéressant de noter ici que NotImplemented
c'est vrai, mais cela aurait été une histoire différente si la classe avait défini une méthode __bool__
ou __len__
qui retournait False
ou 0
respectivement.
Si vous voulez l'équivalent fonctionnel de l' ==
opérateur, utilisez operator.eq
:
import operator
operator.eq(1, 'a')
# False
Cependant, comme mentionné précédemment, pour ce scénario spécifique , dans lequel vous recherchez None
, utilisez is
:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
L'équivalent fonctionnel de ceci utilise operator.is_
:
operator.is_(var2, None)
# True
None
est un objet spécial, et une seule version existe en mémoire à tout moment. IOW, c'est le seul singleton de la NoneType
classe (mais le même objet peut avoir n'importe quel nombre de références). Les directives PEP8 expliquent cela:
Les comparaisons avec des singletons comme None
devraient toujours être faites avec is
ou
is not
, jamais avec les opérateurs d'égalité.
En résumé, pour les singletons aiment None
, une vérification de référence avec is
est plus approprié, même si les deux ==
et is
fonctionnera très bien.