Почему «if not someobj:» лучше, чем «if someobj == None:» в Python?

127

Я видел несколько примеров такого кода:

if not someobj:
    #do something

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

if someobj == None:
    #do something

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

fuentesjr
источник
13
Как правило, someobj is None предпочтительнее someobj == None,
Аарон Маенпаа
как насчет псевдокода, если X не None? Или X != None?
Чарли Паркер

Ответы:

188

В первом тесте 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
Сильвен Дефресн
источник
4
Хотя это технически правильно, это не объясняет, что кортежи, списки, словари, строки, юникоды, целые числа, числа с плавающей запятой и т. Д. Имеют ненулевое значение . Гораздо чаще полагаться на истинностное значение встроенного типа, чем на настраиваемый ненулевой метод.
ddaa
50

На самом деле это плохая практика. Когда-то считалось нормальным небрежно относиться к 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.

С. Лотт
источник
3
Вторая форма - это категорически неплохая практика. PEP 8 рекомендует использовать if x дважды . Сначала для последовательностей (вместо использования len), а затем для True и False (вместо использования is). Практически весь код Python, который я видел, использует if x, а if not x.
Антти Расинен
35

Потому что 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
badp
источник
Что касается вашего последнего вопроса, они равнозначны.
Sylvain Defresne
И оба неверны, потому что "" ложно. Второй должен читать '("",) или ("s",)'. В любом случае, в современных версиях python есть правильный тернарный оператор. Этот способный к ошибкам взлом должен быть исключен.
ddaa
4

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, вам следует использовать логический оператор.

Винко Врсалович
источник
3

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

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

__nonzero__ метод спам вызывается. Из руководства Python:

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

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

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

__eq__ метод спам вызывается с аргументом None .

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

число Пи.
источник
2

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

Zgoda
источник
1
Чтобы быть абсолютно правильным, второй проверяет -equality- со значением None ... "someobj is None" проверяет идентичность.
Мэтью Тревор,
0

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

rslite
источник
0

Ответ - «это зависит от обстоятельств».

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

Маттиас Кестенхольц
источник
0

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

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

PhiLho
источник