Это отличный пример того, почему __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будут работать просто отлично.