jeśli A vs jeśli A nie jest None:

154

Mogę uzyć:

if A:

zamiast

if A is not None:

Ta ostatnia wydaje się taka gadatliwa. Czy jest jakaś różnica?

rbairos
źródło

Odpowiedzi:

149

Twierdzenie

if A:

wywoła A.__nonzero__()(zobacz dokumentację nazw metod specjalnych ) i użyje wartości zwracanej przez tę funkcję. Oto podsumowanie:

object.__nonzero__(self)

Wezwany do wdrożenia testowania wartości prawdy i wbudowanej operacji bool(); powinny zwrócić Falseor True, lub ich odpowiedniki w postaci liczb całkowitych 0lub 1. Gdy ta metoda nie jest zdefiniowana, __len__()jest wywoływana, jeśli jest zdefiniowana, a obiekt jest uważany za prawdziwy, jeśli jego wynik jest różny od zera. Jeśli klasa nie definiuje __len__()ani __nonzero__(), wszystkie jej wystąpienia są uznawane za prawdziwe.

Z drugiej strony,

if A is not None:

porównuje tylko odniesienie Az, Noneaby zobaczyć, czy jest takie samo, czy nie.

Greg Hewgill
źródło
4
i tak A is not Nonejest szybciej, ponieważ jest znacznie mniej do zrobienia
John La Rooy
40
@gnibbler Nie według moich testów. if object(): passwynosi ~ 0,130 usek na pętlę, podczas gdy if object() is not None: passwynosi ~ 0,135 usek. W każdym razie nie powinieneś używać wydajności do wyboru między tymi dwoma, ale raczej przyjrzyj się różnicom w ich działaniu, ponieważ nie są one równoważne .
Lauritz V. Thaulow
1
@gnibbler if A is not Nonewydaje się być wolniejszy, ponieważ jest to porównanie i musi załadować wbudowany singleton Nonejako pośredni krok do porównania A(spójrz na dis.dis()). Popraw mnie, jeśli się mylę, ale if A:wydaje się być skuteczniejszy, gdy tylko naprawdę chcesz sprawdzić wartość prawdy, a nie Nonetożsamość.
cedbeu
2
@cedbeu, wydaje się zależeć od wartości A. Testowałem teraz, python -m timeit -s"a=0" "if a: pass" "else: pass"jest szybszy niż, python -m timeit -s"a=0" "if a is None: pass" "else: pass"ale python -m timeit -s"a=1" "if a: pass" "else: pass"jest wolniejszy. Może być zależne od platformy, zobacz, czy uzyskasz takie same wyniki
John La Rooy
2
@cedbeu, w Pythonie3 wszystkie są znacznie szybsze, ale is Nonetest był dla mnie rzeczywiście najwolniejszy. W pypy wszyscy mierzyli dokładnie tak samo :)
John La Rooy
51

Jak napisano w PEP8 :

  • Porównania z singletonami, takimi jak None, powinny być zawsze przeprowadzane za pomocą „jest” lub „nie jest”, a nie operatorów równości .

    Także strzeżcie piśmie „jeśli x”, gdy naprawdę znaczy „jeśli x nie jest None” - na przykład przy badaniu, czy zmienna lub argument, że domyślnie żadna nie była ustawiona na inną wartość. Druga wartość może mieć typ (taki jak kontener), który może być fałszywy w kontekście logicznym!

scraplesh
źródło
7
Mmmh to nie jest jednak zbyt jasne i nie odpowiada na OP. Automatyczne przekierowanie do PEP nie zawsze jest dobrą odpowiedzią. Bardzo często nie interesuje nas test tożsamości z singletonem None, a jedynie sprawdzenie wartości prawdy. W tym przypadku if A:wydaje się bardziej wydajne (weź dis.dis(), są dodatkowe kroki wczytywania wbudowanego Nonei porównywania if A is not None:, podczas gdy jump_ifw drugim przypadku jest tylko a ).
cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

więc to nie to samo, co

if x is not None # which works only on None
Abdul Muneer
źródło
17

Wiele funkcji zwraca None, jeśli nie ma odpowiednich wyników. Na przykład .first()metoda zapytania SQLAlchemy zwraca wartość None, jeśli w wyniku nie ma żadnych wierszy. Załóżmy, że wybierasz wartość, która może zwrócić 0 i musisz wiedzieć, czy w rzeczywistości jest to 0, czy też zapytanie nie dało żadnych wyników.

Typowym idiomem jest przypisanie opcjonalnemu argumentowi funkcji lub metody domyślnej wartości None, a następnie przetestowanie tej wartości jako None, aby sprawdzić, czy została określona. Na przykład:

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

porównaj to z:

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

W drugim przypadku, co się stanie, jeśli zadzwonisz spam(0)lub spam([])? Funkcja wykryłaby (niepoprawnie), że nie przekazałeś wartości dla eggsi obliczyłaby dla ciebie wartość domyślną. Prawdopodobnie nie tego chcesz.

Albo wyobraź sobie metodę typu „zwróć listę transakcji dla danego konta”. Jeśli konto nie istnieje, może zwrócić Brak. Różni się to od zwrócenia pustej listy (co oznaczałoby, że „to konto istnieje, ale nie zarejestrowało transakcji).

Wreszcie wróćmy do bazy danych. Istnieje duża różnica między wartością NULL a pustym ciągiem. Pusty ciąg zwykle mówi „jest tutaj wartość, a ta wartość jest niczym”. NULL mówi „ta wartość nie została wprowadzona”.

W każdym z tych przypadków chciałbyś użyć if A is None. Sprawdzasz określoną wartość - None - a nie tylko „jakąkolwiek wartość, która zdarzy się rzutować na False”.

Kirk Strauser
źródło
10

Robią bardzo różne rzeczy .

Poniższe sprawdza czy ma nic oprócz wartości False, [], None, ''i 0. Sprawdza wartość A.

if A:

Poniższe sprawdza, czy A jest innym obiektem niż Brak. Sprawdza i porównuje odniesienie (adres pamięci) A i None.

if A is not None:

UPDATE: Dalsze wyjaśnienia

Wiele razy wydaje się, że robią to samo, więc wiele osób używa ich zamiennie - to naprawdę zły pomysł. Powodem, dla którego oba dają te same wyniki, jest wiele razy czysty przypadek z powodu optymalizacji interpretera / kompilatora, takich jak interning lub coś innego.

Mając na uwadze te optymalizacje, liczby całkowite i ciągi o tej samej wartości używają tego samego miejsca w pamięci. To prawdopodobnie wyjaśnia, dlaczego dwa oddzielne ciągi działają tak, jakby były takie same.

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

Jednak inne rzeczy nie zachowują się tak samo.

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

Obie listy najwyraźniej mają swoją własną pamięć. Zaskakująco krotki zachowują się jak struny.

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

Prawdopodobnie wynika to z faktu, że krotki na pewno się nie zmienią, dlatego warto ponownie użyć tej samej pamięci.

Podsumowując, nie można polegać na zbiegach okoliczności . To, że kwacze jak kaczka, nie oznacza, że ​​jest kaczką. Użyj isiw ==zależności od tego, co naprawdę chcesz sprawdzić. Te rzeczy mogą być trudne do debugowania, ponieważ isczyta się jak proza, którą często po prostu przeglądamy.

Pithikos
źródło
Nonejest singletonem, nie jest szczegółem implementacji (w przeciwieństwie do internowania int lub string). Nie jestem pewien, czy rozumiem to, co masz na myśli.
Lev Levitsky
@LevLevitsky nie chodzi mi o to, że Nonezachowuje się inaczej intlub z strpowodu internowania. Chodzi mi o to isi ==sprawdzam różne rzeczy; pierwszy sprawdza adres pamięci, drugi sprawdza zawartość adresów pamięci.
Pithikos,
@LevLevitsky Zredagowałem teraz swoją odpowiedź, aby była bardziej zrozumiała.
Pithikos,
7

if A: okaże się fałszywe, jeśli A ma wartość 0, Fałsz, pusty ciąg, pustą listę lub Brak, co może prowadzić do niepożądanych wyników.

keflavich
źródło
1
I wiele innych wartości, takich jak pusta lista, pusty zbiór, pusta krotka itp. Zasadniczo wszystko, co nie jest zgodne z prawdą według docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod
6

Większość przewodników, które widziałem, sugeruje, że powinieneś użyć

Jeśli:

chyba że masz powód, by być bardziej szczegółowym.

Jest kilka drobnych różnic. Istnieją wartości inne niż None, które zwracają False, na przykład puste listy lub 0, więc zastanów się, do czego naprawdę testujesz.

Colin Coghill
źródło
5

None to specjalna wartość w Pythonie, która zwykle oznacza niezainicjowaną zmienną. Aby sprawdzić, czy A nie ma tej konkretnej wartości, użyj:

if A is not None

Wartości Falsey to specjalna klasa obiektów w Pythonie (np. False, []). Aby sprawdzić, czy A to błąd, użyj:

if not A

Zatem te dwa wyrażenia nie są takie same I lepiej nie traktować ich jako synonimów.


PS Brak jest również błędem, więc pierwsze wyrażenie implikuje drugie. Ale druga obejmuje inne fałszywe wartości oprócz None. Teraz ... jeśli możesz być pewien, że nie możesz mieć innych wartości falsey poza None w A, możesz zastąpić pierwsze wyrażenie drugim.

mircealungu
źródło
Nie chodzi o to, że się mylisz, ale ta odpowiedź już to obejmuje.
Makoto,
boh, chciałbym prosić o wycofanie głosu przeciw, jeśli nie masz nic przeciwko. moja odpowiedź jest równoważna ze wszystkimi innymi, może mniej z tą, do której się odnosisz, ale jest przedstawiona z innej perspektywy i może być po prostu łatwiejsza do zrozumienia dla kogoś. ponadto, kiedy widzę negatywny głos na SO, zakładam złą odpowiedź ... co, przyznajesz, nie jest prawdą.
mircealungu
Cóż, to założenie jest niepełne. Może to być złe lub po prostu nieprzydatne. Ponieważ twoja odpowiedź, tak jak powiedziałem, została już omówiona, nie jestem przekonany, że jest przydatna.
Makoto,
4

To zależy od kontekstu.

Używam, if A:gdy spodziewam Asię, że będzie to jakaś kolekcja, i chcę wykonać blok tylko wtedy, gdy kolekcja nie jest pusta. Dzięki temu dzwoniący może przekazać każdą dobrze wychowaną kolekcję, pustą lub nie, i zrobić to, czego oczekuję. Umożliwia również Nonei Falsewstrzymuje wykonanie bloku, co jest czasami wygodne do wywołania kodu.

OTOH, jeśli spodziewam Asię, że będzie to jakiś całkowicie dowolny obiekt, ale mógł zostać ustawiony domyślnie None, to zawsze używam if A is not None, ponieważ kod wywołujący mógł celowo przekazać odwołanie do pustej kolekcji, pustego ciągu lub typu liczbowego o wartości 0, lub boolean Falselub jakaś instancja klasy, która jest fałszywa w kontekście boolowskim.

Z drugiej strony, jeśli spodziewam Asię, że będzie to coś bardziej szczegółowego (np. Instancja klasy, której mam zamiar wywołać metody), ale mogła Nonezostać ustawiona domyślnie i uważam, że domyślna konwersja boolowska jest właściwość klasy, której nie mam nic przeciwko narzucaniu wszystkim podklasom, wtedy po prostu if A:oszczędzę palcom strasznego ciężaru wpisywania dodatkowych 12 znaków.

Ben
źródło
3

Utworzyłem plik o nazwie test.pyi uruchomiłem go na tłumaczu. Możesz zmienić to, co chcesz, aby sprawdzić na pewno, jak to się dzieje za kulisami.

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)

To jest różnica w assemblerze:

Źródło:

>>> 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'>
użytkownik
źródło
2

Pierwsza jest bardziej Pythonowa (lepszy kod ideomatyczny), ale nie wykona bloku, jeśli A jest fałszywe (nie brak).

Borealid
źródło
7
-1. Jak wspomina @klesh, PEP8 mówi, aby używać is/is not None. Po PEP8 jest Pythonic. Poza tym dwa testy są różne .
JCotton
1

python> = 2.6,

jeśli napiszemy np

if A:

wygeneruje ostrzeżenie,

FutureWarning: zachowanie tej metody zmieni się w przyszłych wersjach. Zamiast tego użyj specjalnego testu „len (elem)” lub „elem is not None”.

Więc możemy użyć

if A is not None:
normalUser
źródło