Я видел несколько примеров такого кода:
if not someobj:
#do something
Но мне интересно, почему бы не сделать:
if someobj == None:
#do something
Есть ли разница? Есть ли у одного преимущество перед другим?
X != None?
Я видел несколько примеров такого кода:
if not someobj:
#do something
Но мне интересно, почему бы не сделать:
if someobj == None:
#do something
Есть ли разница? Есть ли у одного преимущество перед другим?
X != None?
Ответы:
В первом тесте 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
На самом деле это плохая практика. Когда-то считалось нормальным небрежно относиться к 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.
Потому что 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
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, вам следует использовать логический оператор.
Если вы спросите
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
Эти два сравнения служат разным целям. Первый проверяет логическое значение чего-либо, второй проверяет идентичность со значением None.
Ответ - «это зависит от обстоятельств».
Я использую первый пример, если считаю, что 0, "", [] и False (список не исчерпывающий) эквивалентны None в этом контексте.
Лично я выбрал последовательный подход для разных языков: я делаю if (var)(или эквивалент), только если var объявлен как логический (или определен как таковой, в C у нас нет определенного типа). Я даже ставлю перед этими переменными префикс b(так было бы на bVarсамом деле), чтобы быть уверенным, что случайно не использую здесь другой тип.
Мне не нравится неявное приведение к логическому типу, тем более, когда существует множество сложных правил.
Конечно, люди не согласятся. Некоторые идут дальше, я вижу if (bVar == true)в Java-коде в своей работе (слишком избыточный на мой вкус!), Другим нравится слишком компактный синтаксис, идущий while (line = getNextLine())(слишком неоднозначный для меня).