Почему "if not someobj:" лучше, чем "if someobj == None:" в Python?
Я видел несколько примеров кода вроде этого:
if not someobj:
#do something
Но мне интересно, почему не делать:
if someobj == None:
#do something
Есть ли разница? Имеет ли преимущество преимущество над другим?
Ответы
Ответ 1
В первом тесте Python попытается преобразовать объект в значение bool
, если он еще не один. Грубо говоря, мы задаем объект: вы значимы или нет? Это делается с использованием следующего алгоритма:
-
Если у объекта есть специальный метод __nonzero__
(как и числовые встроенные модули, int
и float
), он вызывает этот метод. Он должен либо вернуть значение bool
, которое затем используется напрямую, либо значение int
, которое считается False
, если оно равно нулю.
-
В противном случае, если объект имеет специальный метод __len__
(как и встроенные контейнеры, list
, dict
, set
, tuple
,...), он вызывает это метод, рассматривая контейнер False
, если он пуст (длина равна нулю).
-
В противном случае объект считается True
, если он не является None
, и в этом случае считается False
.
Во втором тесте объект сравнивается для равенства с None
. Здесь мы запрашиваем объект: "Вы равны этому другому значению?" Это выполняется с использованием следующего алгоритма:
-
Если объект имеет метод __eq__
, он вызывается, а возвращаемое значение затем преобразуется в значение bool
и используется для определения результата if
.
-
В противном случае, если объект имеет метод __cmp__
, он вызывается. Эта функция должна возвращать int
, указывающую порядок двух объектов (-1
, если self < other
, 0
, если self == other
, +1
, если self > other
).
-
В противном случае объект сравнивается для идентификации (т.е. они относятся к одному и тому же объекту, который может быть проверен оператором 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
Ответ 2
На самом деле это и плохая практика. Когда-то считалось, что ОК небрежно относится к None и False как к подобному. Однако, поскольку Python 2.2 это не лучшая политика.
Во-первых, когда вы выполняете тест типа if x
или if not x
, Python должен неявно преобразовывать x
в boolean. Правила для функции 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
Потому что 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
, {}
и т.д. все отличаются друг от друга, но все они оцениваются как False.
Это "магия" за короткое замыкание, например:
foo = bar and spam or eggs
который является сокращением для:
if bar:
foo = spam
else:
foo = eggs
хотя вы действительно должны написать:
foo = spam if bar else egg
Ответ 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, вы должны использовать логический оператор.
Ответ 5
Если вы спросите
if not spam:
print "Sorry. No SPAM."
вызывается метод спама __nonzero__. Из руководства Python:
__ __ отлична от нуля (сам) Вызывается для реализации тестирования ценности истины и встроенной операции 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
Ответ 6
Эти два сравнения служат различным целям. Первый проверяет логическое значение чего-то, второй проверяет личность на значение None.
Ответ 7
Для примера первый пример короче и выглядит лучше. В соответствии с другими сообщениями то, что вы выбираете, также зависит от того, что вы действительно хотите сделать для сравнения.
Ответ 8
Ответ: "Это зависит".
Я использую первый пример, если я считаю 0, "", [] и False (список не исчерпывающий) эквивалентным None в этом контексте.
Ответ 9
Лично я выбрал последовательный подход на разных языках: я делаю if (var)
(или эквивалент) только в том случае, если var объявлен как boolean (или определенный как таковой, в C у нас нет определенного типа). Я даже префикс этих переменных с помощью b
(так что это будет bVar
на самом деле), чтобы быть уверенным, что я не буду случайно использовать другой тип здесь.
Мне не очень нравится неявное кастинг для boolean, еще меньше, когда существует множество сложных правил.
Конечно, люди не согласятся. Некоторые идут дальше, я вижу if (bVar == true)
в коде Java на моей работе (слишком избыточный для моего вкуса!), Другие любят слишком большой компактный синтаксис, переходя while (line = getNextLine())
(слишком двусмысленный для меня).