Это отличный пример того, почему __dunder__
методы не должны использоваться напрямую, поскольку они часто не являются подходящими заменами для их эквивалентных операторов; ==
вместо этого вы должны использовать оператор для сравнения на равенство, или в этом особом случае, при проверке None
, использовать is
(перейдите к нижней части ответа для получения дополнительной информации).
Ты сделал
None.__eq__('a')
# NotImplemented
Который возвращается, NotImplemented
так как сравниваемые типы различны. Рассмотрим другой пример, в котором два объекта с разными типами сравниваются таким образом, например, 1
и 'a'
. Делать (1).__eq__('a')
тоже не правильно, и вернусь NotImplemented
. Правильный способ сравнить эти два значения на равенство
1 == 'a'
# False
Что здесь происходит
- Во-первых,
(1).__eq__('a')
попробовал, который возвращает NotImplemented
. Это указывает на то, что операция не поддерживается, поэтому
'a'.__eq__(1)
называется, который также возвращает то же самое NotImplemented
. Так,
- Объекты обрабатываются так, как будто они не совпадают, и
False
возвращаются.
Вот хороший маленький MCVE, использующий некоторые пользовательские классы, чтобы проиллюстрировать, как это происходит:
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
Конечно, это не объясняет, почему операция возвращает true. Это потому, что NotImplemented
на самом деле истинная ценность:
bool(None.__eq__("a"))
# True
Такой же как,
bool(NotImplemented)
# True
Для получения дополнительной информации о том, какие значения считаются правдивыми и ложными, см. Раздел «Документы», посвященный проверке истинности значений , а также этот ответ . Здесь стоит отметить, что NotImplemented
это правдиво, но это была бы другая история, если бы класс определил a __bool__
или __len__
метод, который возвратил False
или 0
соответственно.
Если вы хотите функциональный эквивалент ==
оператора, используйте operator.eq
:
import operator
operator.eq(1, 'a')
# False
Однако, как упоминалось ранее, для этого конкретного сценария , в котором вы проверяете None
, используйте is
:
var = 'a'
var is None
# False
var2 = None
var2 is None
# True
Функциональный эквивалент этого использует operator.is_
:
operator.is_(var2, None)
# True
None
является специальным объектом, и в любой момент времени в памяти существует только 1 версия. IOW, это единственный синглтон NoneType
класса (но один и тот же объект может иметь любое количество ссылок). В рекомендации PEP8 сделать это явно:
Сравнения с синглетонами, такими как None
всегда, должны выполняться с is
или
is not
никогда, а не с операторами равенства.
Таким образом, для синглтонов, таких как None
, проверка ссылки с is
более подходящим, хотя оба ==
и is
будут работать просто отлично.