Почему `if None .__ eq __ (" a ")`, кажется, оценивается как True (но не совсем)? - PullRequest
0 голосов
/ 31 декабря 2018

Если вы выполните следующую инструкцию в Python 3.7, она (из моего тестирования) напечатает b:

if None.__eq__("a"):
    print("b")

Однако None.__eq__("a") оценивается как NotImplemented.

Естественно, "a".__eq__("a") оценивается в True, а "b".__eq__("a") оценивается в False.

Я первоначально обнаружил это при тестировании возвращаемого значения функции, но ничего не возвращал во второйcase - так, функция вернула None.

Что здесь происходит?

Ответы [ 4 ]

0 голосов
/ 28 января 2019

Почему?

Возвращает NotImplemented, ага:

>>> None.__eq__('a')
NotImplemented
>>> 

Но если вы посмотрите на это:

>>> bool(NotImplemented)
True
>>> 

NotImplemented на самом делеистинное значение, поэтому оно возвращает b, все, что является True, пройдет, все, что будет False, не будет.

Как решить это?

Вынужно проверить, если это True, так что будьте более подозрительны, как вы видите:

>>> NotImplemented == True
False
>>> 

Итак, вы бы сделали:

>>> if None.__eq__('a') == True:
    print('b')


>>> 

И, как вы видите, это не будетничего не вернуть.

0 голосов
/ 31 декабря 2018

Результат, который вы видите, вызван тем фактом, что

None.__eq__("a") # evaluates to NotImplemented

оценивается как NotImplemented, а истинное значение NotImplemented задокументировано как True:

https://docs.python.org/3/library/constants.html

Специальное значение, которое должно быть возвращено двоичными специальными методами (например, __eq__(), __lt__(), __add__(), __rsub__() и т. Д.), Чтобы указать, что операцияне реализован по отношению к другому типу;могут быть возвращены бинарными специальными методами (например, __imul__(), __iand__() и т. д.) для той же цели. Его истинное значение истинно.

Если вы вызываете метод __eq()__ вручную, а не просто используете ==, вам нужно быть готовым к тому, что он можетвернуть NotImplemented и что его значение истинности истинно.

0 голосов
/ 31 декабря 2018

Как вы уже поняли, None.__eq__("a") оценивается как NotImplemented, однако, если вы попробуете что-то вроде

if NotImplemented:
    print("Yes")
else:
    print("No")

, результат будет

да

это означает, что значение истинности NotImplemented true

Поэтому исход вопроса очевиден:

None.__eq__(something) дает NotImplemented

Аbool(NotImplemented) оценивается как True

Так что if None.__eq__("a") всегда True

0 голосов
/ 31 декабря 2018

Это прекрасный пример того, почему методы __dunder__ не должны использоваться напрямую, поскольку они часто не являются подходящими заменами для их эквивалентных операторов;вместо этого следует использовать оператор == для сравнения на равенство, или в этом особом случае при проверке на None используйте is (для получения дополнительной информации перейдите к нижней части ответа).

Вы сделали

None.__eq__('a')
# NotImplemented

, который возвращает NotImplemented, поскольку сравниваемые типы различны.Рассмотрим другой пример, в котором два объекта с разными типами сравниваются таким образом, например 1 и 'a'.Выполнение (1).__eq__('a') также некорректно и вернет NotImplemented.Правильный способ сравнить эти два значения на равенство будет

1 == 'a'
# False

. Здесь происходит следующее:

  1. Сначала выполняется попытка (1).__eq__('a'), которая возвращает NotImplemented.Это указывает на то, что операция не поддерживается, поэтому вызывается
  2. 'a'.__eq__(1), который также возвращает тот же NotImplemented.Итак,
  3. Объекты обрабатываются так, как будто они не совпадают, и возвращается 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 будут работать просто отлично.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...