Це чудовий приклад того, чому __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
Звичайно, це не пояснює, чому операція повертає істину. Це тому 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
буде працювати чудово.