если A против, если A не None:

154

Могу ли я использовать:

if A:

вместо того

if A is not None:

Последнее кажется таким многословным. Есть ли разница?

rbairos
источник

Ответы:

149

Заявление

if A:

будет вызывать A.__nonzero__()(см. документацию по именам специальных методов ) и использовать возвращаемое значение этой функции. Вот резюме:

object.__nonzero__(self)

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

С другой стороны,

if A is not None:

сравнивает только ссылку Aс тем, Noneчтобы увидеть, является ли это то же самое или нет.

Грег Хьюгилл
источник
4
и так A is not Noneбыстрее, так как работы намного меньше
Джон Ла Рой
40
@gnibbler Не по моим тестам. if object(): pass~ 0,130 мксек на цикл, а if object() is not None: pass~ 0,135 мксек. В любом случае, вам не следует использовать производительность, чтобы выбирать между этими двумя, а скорее смотреть на различия в том, как они работают, поскольку они не эквивалентны .
Lauritz V. Thaulow
1
@gnibbler, if A is not Noneкажется, медленнее, потому что это сравнение, и ему нужно загрузить встроенный синглтон Noneв качестве промежуточного шага для сравнения A(взгляните на dis.dis()). Поправь меня, если я ошибаюсь, но, if A:кажется, это более эффективно, как только ты действительно хочешь проверить ценность истины, а не Noneличность.
cedbeu
2
@cedbeu, кажется, зависит от значения A. Я тестировал сейчас python -m timeit -s"a=0" "if a: pass" "else: pass"быстрее, python -m timeit -s"a=0" "if a is None: pass" "else: pass"но python -m timeit -s"a=1" "if a: pass" "else: pass"медленнее. Может зависеть от платформы, посмотреть, если вы получите те же результаты
Джон Ла Руи
2
@cedbeu, в Python3 они все намного быстрее, но is Noneтест был действительно самым медленным для меня. В pypy они все измерены абсолютно одинаково :)
John La Rooy
51

Как написано в PEP8 :

  • Сравнения с синглетами, такими как None, всегда следует выполнять с помощью «is» или «is not», а не операторов равенства .

    Кроме того, остерегайтесь писать «если x», когда вы действительно имеете в виду «если x не None» - например, при проверке, было ли для переменной или аргумента, которое по умолчанию None, установлено какое-то другое значение. Другое значение может иметь тип (например, контейнер), который может быть ложным в логическом контексте!

scraplesh
источник
7
Мммм, это не очень понятно, и не отвечает на ФП. Автоматическое перенаправление на PEP не всегда хороший ответ. Очень часто кто-то интересуется не тестом идентичности с синглтоном None, а просто проверкой истинности. В этом случае if A:кажется более эффективным (принять dis.dis(), есть дополнительные шаги загрузки встроенного Noneи сравнения, с if A is not None:, в то время как есть только jump_ifв другом случае).
cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

так что это не то же самое, что

if x is not None # which works only on None
Абдул Мьюнер
источник
17

Многие функции возвращают None, если нет подходящих результатов. Например, .first()метод запроса SQLAlchemy возвращает None, если в результате не было строк. Предположим, вы выбираете значение, которое может возвращать 0, и вам необходимо знать, действительно ли оно равно 0 или запрос не дал результатов вообще.

Распространенной идиомой является присвоение необязательному аргументу функции или метода значения по умолчанию None, а затем проверка значения None, чтобы убедиться, что оно указано. Например:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

сравните это с:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

В последнем случае, что произойдет, если вы позвоните spam(0)или spam([])? Функция (неправильно) обнаружит, что вы не передали значение, eggsи вычислит значение по умолчанию для вас. Это, вероятно, не то, что вы хотите.

Или представьте себе такой метод, как «вернуть список транзакций для данного аккаунта». Если аккаунт не существует, он может вернуть None. Это отличается от возврата пустого списка (что означало бы, что «эта учетная запись существует, но не записала транзакции).

Наконец, вернемся к материалам базы данных. Существует большая разница между NULL и пустой строкой. Пустая строка обычно говорит: «здесь есть значение, а это значение вообще ничего». NULL говорит: «это значение не было введено».

В каждом из этих случаев вы бы хотели использовать if A is None. Вы проверяете конкретное значение - None - не просто «любое значение, приведенное к False».

Кирк Штраузер
источник
10

Они делают разные вещи .

Приведенные ниже проверки , если А имеет ничего , кроме значений False, [], None, ''и 0. Проверяет значение А.

if A:

Ниже проверяется, является ли объект A отличным от None. Он проверяет и сравнивает ссылку (адрес памяти) A и None.

if A is not None:

ОБНОВЛЕНИЕ: дальнейшее объяснение

Часто кажется, что они делают одно и то же, поэтому многие люди используют их взаимозаменяемо - это действительно плохая идея. Причина, по которой эти два результата дают одни и те же, много раз по чистой случайности из-за оптимизаций интерпретатора / компилятора, таких как интернирование или что-то еще.

С учетом этих оптимизаций целые и строки одного и того же значения в конечном итоге используют одно и то же пространство памяти. Это, вероятно, объясняет, почему две отдельные строки действуют так, как если бы они были одинаковыми.

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

Другие вещи не ведут себя так же, хотя ..

> a = []
> b = []
> a is b
False
> a == b
True

У двух списков явно есть своя память. Удивительно, но кортежи ведут себя как строки.

> a = ()
> b = ()
> a is b
True
> a == b
True

Вероятно, это потому, что кортежи гарантированно не изменятся, поэтому имеет смысл повторно использовать одну и ту же память.

Суть в том, что нельзя полагаться на совпадения . То, что это крякает как утка, не означает, что это утка. Используйте isи в ==зависимости от того, что вы действительно хотите проверить. Эти вещи трудно отладить, так isкак они читаются как проза, и мы часто просто просматриваем их.

Pithikos
источник
Noneэто синглтон, это не деталь реализации (в отличие от int или интернирования строк). Я не уверен, что понимаю, что вы имеете в виду.
Лев Левицкий
@LevLevitsky моя точка зрения не то, что Noneведет себя иначе, чем intили strиз-за интернирования. Моя точка зрения в том, что isи ==проверяют разные вещи; первый проверяет адрес памяти, второй проверяет содержимое адресов памяти.
Питикос,
@LevLevitsky Теперь я отредактировал свой ответ, чтобы сделать его немного удобнее.
Питикос,
7

if A: будет иметь значение false, если A равно 0, False, пустая строка, пустой список или None, что может привести к нежелательным результатам.

keflavich
источник
1
И многие другие значения, такие как пустой список, пустой набор, пустой кортеж и т. Д. По сути, все, что не соответствует действительности, в соответствии с docs.python.org/3/library/stdtypes.html#truth-value-testing .
Джармод
6

Большинство руководств, которые я видел, предлагают вам использовать

если:

если у вас нет причин быть более конкретными.

Есть небольшие отличия. Существуют значения, отличные от None, которые возвращают False, например, пустые списки или 0, поэтому подумайте, что именно вы проверяете.

Колин Когхилл
источник
5

None - это специальное значение в Python, которое обычно обозначает неинициализированную переменную. Чтобы проверить, нет ли у A конкретного значения, которое вы используете:

if A is not None

Значения Falsey - это особый класс объектов в Python (например, false, []). Чтобы проверить, является ли A ложным, используйте:

if not A

Таким образом, эти два выражения не совпадают, и вам лучше не рассматривать их как синонимы.


PS Нет, это тоже фальшивка, поэтому первое выражение подразумевает второе. Но вторая охватывает другие значения фальши, кроме None. Теперь ... если вы можете быть уверены, что у вас не может быть других значений Falsey, кроме None в A, то вы можете заменить первое выражение вторым.

mircealungu
источник
Это не значит, что вы не правы, но этот ответ уже охватывает это.
Макото
Бо, я бы хотел попросить тебя убрать понижение, если ты не против. мой ответ эквивалентен всем остальным, может быть, меньше того, на который вы ссылаетесь, но представлен с другой точки зрения, и для кого-то это может быть проще для понимания. более того, когда я вижу отрицательное мнение о SO, я предполагаю неправильный ответ ... что, как вы признаете, не так.
mircealungu
Ну, это предположение не является полным. Это может быть неправильно или просто не полезно. Поскольку ваш ответ, как я уже говорил, уже освещен, я не уверен, что он полезен.
Макото
4

Это зависит от контекста.

Я использую, if A:когда я ожидаю, Aчто будет какая-то коллекция, и я хочу выполнить блок, только если коллекция не пустая. Это позволяет вызывающей стороне передавать любую коллекцию с хорошим поведением, пустую или нет, и делать так, как я ожидаю. Это также позволяет Noneи Falseподавлять выполнение блока, что иногда удобно для вызова кода.

OTOH, если я ожидаю, Aчто какой-то совершенно произвольный объект, но он мог бы быть по умолчанию None, то я всегда использую if A is not None, так как вызывающий код мог преднамеренно передать ссылку на пустую коллекцию, пустую строку или 0-значный числовой тип, или логический False, или некоторый экземпляр класса, который оказывается ложным в логическом контексте.

И с другой стороны, если я ожидаю, что Aэто будет более конкретная вещь (например, экземпляр класса, для которого я собираюсь вызывать методы), но его можно было бы использовать по умолчанию None, и я считаю, что логическое преобразование по умолчанию является свойство класса, которое я не прочь применить на всех подклассах, тогда я просто буду использовать, if A:чтобы избавить мои пальцы от ужасного бремени ввода дополнительных 12 символов.

Бен
источник
3

Я создал файл с именем test.pyи запустил его на интерпретаторе. Вы можете изменить то, что хотите, чтобы точно проверить, как идут дела за кулисами.

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

В этом отличие ассемблера:

Источник:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>
пользователь
источник
2

Первый более Pythonic (лучший идеоматический код), но не будет выполнять блок, если A False (не None).

Borealid
источник
7
-1. Как упоминает @klesh, PEP8 говорит об использовании is/is not None. Следующий PEP8 - Pythonic. К тому же два теста разные .
Джоттон
1

python> = 2.6,

если мы напишем такие как

if A:

будет генерировать предупреждение как,

FutureWarning: поведение этого метода будет меняться в будущих версиях. Вместо этого используйте специальный тест «len (elem)» или «elem is None».

Таким образом, мы можем использовать

if A is not None:
normalUser
источник