Почему в Python «если не someobj:» лучше, чем «if someobj == None:»? - PullRequest
113 голосов
/ 19 сентября 2008

Я видел несколько примеров кода, подобных этому:

if not someobj:
    #do something

Но мне интересно, почему не делают:

if someobj == None:
    #do something

Есть ли разница? Есть ли у одного преимущество над другим?

Ответы [ 9 ]

172 голосов
/ 19 сентября 2008

В первом тесте Python пытается преобразовать объект в значение bool, если оно еще не было. Грубо говоря, мы спрашиваем объект: вы значите или нет? Это делается с помощью следующего алгоритма:

  1. Если объект имеет специальный метод __nonzero__ (как и числовые встроенные модули, int и float), он вызывает этот метод. Он должен либо возвращать значение bool, которое затем используется напрямую, либо значение int, которое считается False, если оно равно нулю.

  2. В противном случае, если у объекта есть специальный метод __len__ (как встроенные контейнеры, list, dict, set, tuple, ...), он вызывает это метод с учетом контейнера False, если он пуст (длина равна нулю).

  3. В противном случае объект считается True, если только он не равен None, в этом случае он считается False.

Во втором тесте объект сравнивается на равенство с None. Здесь мы задаем объекту: «Вы равны этому другому значению?» Это делается с использованием следующего алгоритма:

  1. Если у объекта есть метод __eq__, он вызывается, и возвращаемое значение затем преобразуется в значение bool и используется для определения результата if.

  2. В противном случае, если у объекта есть метод __cmp__, он вызывается. Эта функция должна возвращать int, указывающий порядок двух объектов (-1, если self < other, 0, если self == other, +1, если self > other).

  3. В противном случае объект сравнивается на предмет идентичности (т. Е. Он является ссылкой на тот же объект, что может быть проверено оператором is).

Возможен еще один тест с использованием оператора is. Мы бы спросили у объекта: «Вы именно этот объект?»

Как правило, я бы рекомендовал использовать первый тест с нечисловыми значениями, использовать тест на равенство, когда вы хотите сравнить объекты одинаковой природы (две строки, два числа, ...) и проверить идентичность только при использовании значений часового значения (None означает, что не инициализировано для поля-члена для примера или при использовании методов getattr или __getitem__).

Подводя итог, мы имеем:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True
45 голосов
/ 19 сентября 2008

Это на самом деле обе плохие практики. Давным-давно считалось нормальным относиться к None и False как к одинаковым. Однако, начиная с Python 2.2, это не лучшая политика.

Во-первых, когда вы выполняете тест if x или if not x, Python должен неявно преобразовать x в логическое значение. Правила для функции bool описывают множество ложных вещей; все остальное правда. Если значение x не было должным образом логическим для начала, это неявное преобразование на самом деле не самый ясный способ сказать что-либо.

До Python 2.2 функция bool отсутствовала, поэтому она была еще менее понятной.

Во-вторых, вы не должны действительно тестировать с == None. Вы должны использовать is None и is not None.

См. PEP 8, Руководство по стилю для кода Python .

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

Сколько там синглетонов? Пять: None, True, False, NotImplemented и Ellipsis. Поскольку вы действительно вряд ли будете использовать NotImplemented или Ellipsis и никогда не скажете if x is True (потому что просто if x намного понятнее), вы будете когда-либо тестировать только None.

32 голосов
/ 19 сентября 2008

Потому что None не единственное, что считается ложным.

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False, 0, (), [], {} и "" все отличаются от None, поэтому ваши два фрагмента кода не эквивалентны.

Кроме того, учтите следующее:

>>> False == 0
True
>>> False == ()
False

if object: - это , а не проверка на равенство. 0, (), [], None, {} и т. Д. все отличаются друг от друга, но все они оценивают в Ложь. *

Это «магия» за короткими замыканиями, такими как:

foo = bar and spam or eggs

, что сокращенно для:

if bar:
    foo = spam
else:
    foo = eggs

хотя вы действительно должны написать:

foo = spam if bar else egg
3 голосов
/ 19 сентября 2008

Если спросить

if not spam:
    print "Sorry. No SPAM."

__ ненулевой __ метод спам вызывается. Из руководства по Python:

__ ненулевая __ ( самостоятельно ) Вызывается для реализации проверки истинности значения и встроенной операции bool (); должен вернуть False или True или их целочисленные эквиваленты 0 или 1. Если этот метод не определен, вызывается __len __ (), если он определен (см. ниже). Если класс не определяет ни __len __ (), ни __nonzero __ (), все его экземпляры считаются истинными.

Если вы спросите

if spam == None:
    print "Sorry. No SPAM here either."

метод __ eq __ spam вызывается с аргументом None .

Для получения дополнительной информации о возможностях настройки смотрите документацию Python по адресу https://docs.python.org/reference/datamodel.html#basic-customization

3 голосов
/ 19 сентября 2008

PEP 8 - Руководство по стилю для кода Python рекомендует использовать это или не , если вы тестируете на None-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

С другой стороны, если вы тестируете больше, чем None-ness, вы должны использовать логический оператор.

2 голосов
/ 19 сентября 2008

Эти два сравнения служат разным целям. Первый проверяет логическое значение чего-либо, второй проверяет идентичность со значением None.

0 голосов
/ 19 сентября 2008

Лично я выбрал согласованный подход для разных языков: я делаю if (var) (или эквивалентный) только в том случае, если var объявлен как логический (или определен как таковой, в C у нас нет определенного типа). Я даже ставлю эти переменные перед b (так что на самом деле это будет bVar), чтобы быть уверенным, что я не буду случайно использовать другой тип здесь.
Мне не очень нравится неявное приведение к логическому, тем более, когда существует множество сложных правил.

Конечно, люди не согласятся. Некоторые идут дальше, я вижу if (bVar == true) в коде Java на моей работе (слишком избыточно на мой вкус!), Другие любят слишком много компактного синтаксиса, переходя на while (line = getNextLine()) (слишком неоднозначно для меня).

0 голосов
/ 19 сентября 2008

Ответ "это зависит".

Я использую первый пример, если считаю 0, "", [] и False (список не исчерпывающий) эквивалентным None в этом контексте.

0 голосов
/ 19 сентября 2008

Например, первый пример короче и выглядит лучше. Что касается других постов, то, что вы выберете, также зависит от того, что вы действительно хотите сделать со сравнением.

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