якщо A проти, якщо A немає Ні:


154

Чи можу я використовувати:

if A:

замість

if A is not None:

Останнє здається таким багатослівним. Чи є різниця?

Відповіді:


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 Usec на цикл, тоді if object() is not None: passяк ~ 0,135 Usec. У будь-якому випадку, ви не повинні використовувати продуктивність для вибору між цими двома, а краще дивитися на відмінності в тому, як вони працюють, оскільки вони не є рівнозначними .
Lauritz V. Thaulow

1
@gnibbler, if A is not Noneздається, повільніше, тому що це порівняння і потрібно завантажити вбудований синглтон Noneяк посередницький крок для порівняння A(подивіться на dis.dis()). Виправте мене, якщо я помиляюся, але if A:здається, що це більш ефективно, як тільки ви дійсно хочете перевірити цінність істини, а не Noneособистість.
cedbeu

2
@cedbeu, Схоже, залежить від значення А. Я протестував зараз 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 всі вони вимірювали абсолютно однаково :)
Джон Ла Рой

51

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

  • Порівняння одинакових, таких як None, завжди слід робити з "є" або "ні", ніколи оператори рівності .

    Крім того, будьте обережні, щоб написати "if x", якщо ви дійсно маєте на увазі "якщо x - немає None" - наприклад, під час тестування, чи змінна або аргумент, який за замовчуванням не був встановлений на якесь інше значення. Інше значення може мати тип (наприклад, контейнер), який може бути помилковим у булевому контексті!


7
Мммм, це не дуже зрозуміло і не відповідає на ОП. Автоматичне перенаправлення на PEP - це не завжди хороша відповідь. Дуже часто когось цікавить не тест на особистість з одиночним None, а просто перевірка цінності правди. У цьому випадку if A:здається більш ефективним (наприклад dis.dis(), є додаткові кроки завантаження вбудованого Noneта порівняння з if A is not None:, jump_ifв іншому випадку є лише a ).
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

Багато функцій повертає Жодну, якщо немає відповідних результатів. Наприклад, запит SQLAlchemy.first() повертає 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читається як проза, про яку ми часто просто перебираємо її.


Noneє синглтон, це не детальна інформація про реалізацію (на відміну від int або string interning). Я не впевнений, що я слідкую за тим, що ти маєш на увазі.
Лев Левицький

@LevLevitsky моя крапка не то, що Noneповодиться інакше , ніж intабо strз - за інтернування. Суть в тому , що isі ==перевіряють різні речі; перший перевіряє адресу пам'яті, другий перевіряє вміст адрес пам'яті.
Пітікос

@LevLevitsky Я змінив свою відповідь зараз, щоб зробити її трохи засвоюванішою.
Пітікос

7

if A: виявиться помилковим, якщо A дорівнює 0, False, порожній рядок, порожній список або None, що може призвести до небажаних результатів.


1
І багато інших значень, таких як порожній список, порожній набір, порожній кортеж тощо. По суті, все, що не є правдою в docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod

6

Більшість посібників, яких я бачив, пропонують використовувати

якщо:

якщо у вас немає причини бути більш конкретним.

Є деякі незначні відмінності. Є інші значення, крім None, які повертають помилкові, наприклад, порожні списки або 0, тому подумайте, для чого це ви насправді тестуєте.


5

Ніхто не є спеціальним значенням у Python, яке зазвичай позначає неініціалізовану змінну. Щоб перевірити, чи не має A таке конкретне значення, ви використовуєте:

if A is not None

Значення Фальсі - це особливий клас об’єктів у Python (наприклад, false, []). Щоб перевірити, чи є фальсифікатом A:

if not A

Таким чином, два вирази не однакові, і вам краще не ставитися до них як до синонімів.


PS None також не є фальсією, тому перший вираз передбачає другий. Але друга охоплює інші значення фальси, крім None. Тепер ... якщо ви можете бути впевнені, що ви не можете мати інших значень фальси, крім None в A, тоді ви можете замінити перший вираз другим.


Справа не в тому, що ви помиляєтесь, але ця відповідь вже охоплює це.
Макото

бо, я хотів би попросити вас зняти поточний запис, якщо ви не заперечуєте. моя відповідь рівнозначна всім іншим, може бути, меншим, ніж тому, про який ви посилаєтесь, але подається з іншого погляду, і це може бути просто легше зрозуміти для когось. крім того, коли я бачу зворотну заяву на ТА, я припускаю неправильну відповідь ... що ви визнаєте, це не так.
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

Перший є більш пітонічним (краще ідеоматичним кодом), але не виконує блок, якщо A є помилковим (не None).


7
-1. Як згадує @klesh, PEP8 каже використовувати is/is not None. Слідом за PEP8 є пітонічним. До того ж два тести різні .
JCotton

1

пітон> = 2,6,

якщо ми пишемо такі як

if A:

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

FutureWarning: У наступних версіях поведінка цього методу зміниться. Використовуйте певний тест 'len (elem)' або 'elem is no None'.

Тож ми можемо використовувати

if A is not None:
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.