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