Odpowiedzi:
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ćFalseorTrue, lub ich odpowiedniki w postaci liczb całkowitych0lub1. 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.
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 .
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ść.
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
is Nonetest był dla mnie rzeczywiście najwolniejszy. W pypy wszyscy mierzyli dokładnie tak samo :)
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!
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 ).
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
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”.
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.
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.
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.
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.
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.
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.
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.
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'>
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:
A is not Nonejest szybciej, ponieważ jest znacznie mniej do zrobienia