Dlaczego „if not someobj:” jest lepsze niż „if someobj == None:” w Pythonie?

131

Widziałem kilka przykładów takiego kodu:

if not someobj:
    #do something

Ale zastanawiam się, dlaczego nie robić:

if someobj == None:
    #do something

Czy jest jakaś różnica? Czy jeden ma przewagę nad drugim?

fuentesjr
źródło
14
Generalnie „someobj is None” jest lepsze niż „someobj == None”
Aaron Maenpaa
a co z pseudokodem, jeśli X to nie None? Albo X != None?
Charlie Parker

Odpowiedzi:

192

W pierwszym teście Python próbuje przekonwertować obiekt na boolwartość, jeśli jeszcze nią nie jest. Z grubsza pytamy obiekt: czy masz znaczenie, czy nie? Odbywa się to za pomocą następującego algorytmu:

  1. Jeśli obiekt ma __nonzero__specjalną metodę (podobnie jak wbudowane numeryczne inti float), wywołuje tę metodę. Musi albo zwrócić boolwartość, która jest następnie używana bezpośrednio, albo intwartość, która jest uważana za Falserówną zero.

  2. W przeciwnym razie, jeśli obiekt posiada __len__specjalną metodę (jak zrobić Zabudowy kontenerowe, list, dict, set, tuple, ...), wywołuje tę metodę, zważywszy pojemnik Falsejeśli jest pusta (długość wynosi zero).

  3. W przeciwnym razie obiekt jest brany pod uwagę, Truechyba że Nonew takim przypadku jest brany pod uwagę False.

W drugim teście obiekt jest porównywany pod kątem równości z None. Tutaj pytamy obiekt: „Czy jesteś równy tej innej wartości?” Odbywa się to za pomocą następującego algorytmu:

  1. Jeśli obiekt ma __eq__metodę, jest wywoływany, a wartość zwracana jest następnie konwertowana na boolwartość i używana do określenia wyniku if.

  2. W przeciwnym razie, jeśli obiekt ma __cmp__metodę, jest wywoływany. Ta funkcja musi zwrócić intwskazanie kolejności dwóch obiektów ( -1if self < other, 0if self == other, +1if self > other).

  3. W przeciwnym razie obiekty są porównywane pod kątem tożsamości (tj. Odnoszą się do tego samego obiektu, który może być testowany przez isoperatora).

Istnieje możliwość innego testu przy użyciu isoperatora. Pytalibyśmy obiekt: „Czy jesteś tym konkretnym obiektem?”

Generalnie radziłbym użyć pierwszego testu z wartościami nienumerycznymi, użyć testu równości, gdy chcesz porównać obiekty o tej samej naturze (dwa łańcuchy, dwie liczby, ...) i sprawdzić tożsamość tylko wtedy, gdy przy użyciu wartości wartowniczych ( Noneco oznacza, że ​​nie zainicjowano pola członkowskiego na przykład lub podczas korzystania getattrz __getitem__metod lub ).

Podsumowując, mamy:

>>> 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
Sylvain Defresne
źródło
4
Chociaż technicznie poprawne, nie wyjaśnia to, że krotki, listy, dykty, ciągi, unicodes, int, float, itp. Mają wartość niezerową . O wiele bardziej powszechne jest poleganie na wartości prawdziwości typu wbudowanego niż na niestandardowej metodzie niezerowej .
ddaa,
52

W rzeczywistości są to obie słabe praktyki. Dawno, dawno temu uznano, że od niechcenia żadne i fałsz nie będą traktowane jako podobne. Jednak od czasu Pythona 2.2 nie jest to najlepsza zasada.

Po pierwsze, kiedy wykonujesz test if xlub if not xrodzaj testu, Python musi niejawnie przekonwertować xna wartość logiczną. Reguły tej boolfunkcji opisują wiele rzeczy, które są fałszywe; wszystko inne jest prawdą. Jeśli wartość x nie była właściwie logiczna na początku, ta niejawna konwersja nie jest tak naprawdę najlepszym sposobem na powiedzenie rzeczy.

Przed Pythonem 2.2 nie było funkcji bool, więc było to jeszcze mniej jasne.

Po drugie, tak naprawdę nie powinieneś testować == None. Powinieneś użyć is Nonei is not None.

Zobacz PEP 8, Style Guide for Python Code .

- 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!

Ile jest singletonów? Pięć: None, True, False, NotImplementedi Ellipsis. Ponieważ jest mało prawdopodobne, że użyjesz NotImplementedlub Ellipsis, i nigdy byś tego nie powiedział if x is True(ponieważ if xjest po prostu o wiele bardziej przejrzysty), będziesz testować tylko None.

S.Lott
źródło
3
Druga forma to zdecydowanie niezła praktyka. PEP 8 zaleca dwukrotne użycie if x . Najpierw dla sekwencji (zamiast używać len), a następnie dla True i False (zamiast używać is). Praktycznie cały kod Pythona, który widziałem, używa if x, a jeśli nie x.
Antti Rasinen,
36

Ponieważ Nonenie jest to jedyna rzecz, która jest uważana za fałszywą.

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, (), [], {}I ""są różne od None, dzięki czemu dwa fragmenty kodu są nie równoważne.

Ponadto weź pod uwagę następujące kwestie:

>>> False == 0
True
>>> False == ()
False

if object:to nie sprawdzian równość. 0, (), [], None, {}, Itd. bardzo różne od siebie, ale wszystkie one ocenić False.

Na tym polega „magia” wyrażeń zwierających, takich jak:

foo = bar and spam or eggs

co jest skrótem dla:

if bar:
    foo = spam
else:
    foo = eggs

chociaż naprawdę powinieneś napisać:

foo = spam if bar else egg
badp
źródło
Jeśli chodzi o twoje ostatnie pytanie, są one równoważne.
Sylvain Defresne,
I oba są niepoprawne, ponieważ „” jest fałszywe. Drugi powinien brzmieć „(„ ”,) lub („ s ”,)”. W każdym razie, nowoczesne wersje Pythona mają właściwy operator trójskładnikowy. Ten podatny na błędy hack powinien zostać usunięty.
ddaa
4

PEP 8 - Style Guide for Python Code, które zaleca użycie jest lub nie, jeśli testujesz na brak

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

Z drugiej strony, jeśli testujesz dla więcej niż Brak, powinieneś użyć operatora boolowskiego.

Vinko Vrsalovic
źródło
3

Jeśli zapytasz

if not spam:
    print "Sorry. No SPAM."

__nonzero__ metoda spam zostanie wywołany. Z podręcznika Pythona:

__nonzero__ ( self ) Wywoływana w celu zaimplementowania testowania wartości prawdy i wbudowanej operacji bool (); powinna zwracać False lub True, lub ich odpowiedniki w postaci liczb całkowitych 0 lub 1. Gdy ta metoda nie jest zdefiniowana, wywoływana jest funkcja __len __ (), jeśli została zdefiniowana (patrz poniżej). Jeśli klasa nie definiuje ani __len __ (), ani __nonzero __ (), wszystkie jej instancje są uznawane za prawdziwe.

Jeśli zapytasz

if spam == None:
    print "Sorry. No SPAM here either."

__eq__ metoda spam zostanie wywołany z argumentem Żaden .

Aby uzyskać więcej informacji na temat możliwości dostosowywania, zapoznaj się z dokumentacją Pythona pod adresem https://docs.python.org/reference/datamodel.html#basic-customization

Liczba Pi.
źródło
2

Te dwa porównania służą różnym celom. Pierwsza sprawdza wartość logiczną czegoś, druga sprawdza tożsamość z wartością Brak.

zgoda
źródło
1
Aby być absolutnie poprawnym, drugi sprawdza -equality- z wartością None ... „someobj is None” sprawdza tożsamość.
Matthew Trevor,
0

Po pierwsze, pierwszy przykład jest krótszy i ładniejszy. Podobnie jak w innych postach, to, co wybierzesz, zależy również od tego, co naprawdę chcesz zrobić z porównaniem.

rslite
źródło
0

Odpowiedź brzmi: „to zależy”.

Używam pierwszego przykładu, jeśli uznam, że 0, "", [] i False (lista nie jest wyczerpująca) są równoważne z None w tym kontekście.

Matthias Kestenholz
źródło
0

Osobiście wybrałem spójne podejście w różnych językach: robię to if (var)(lub równoważne) tylko wtedy, gdy var jest zadeklarowane jako logiczne (lub zdefiniowane jako takie, w C nie mamy określonego typu). Nawet poprzedzam te zmienne przedrostkiem b(tak byłoby w bVarrzeczywistości), aby mieć pewność, że przypadkowo nie użyję tutaj innego typu.
Nie lubię niejawnego rzutowania na boolean, tym bardziej, gdy istnieje wiele złożonych reguł.

Oczywiście ludzie się nie zgodzą. Niektórzy idą dalej, widzę if (bVar == true)w kodzie Java w mojej pracy (zbyt zbędny jak na mój gust!), Inni uwielbiają zbyt zwartą składnię, idą while (line = getNextLine())(zbyt niejednoznaczne dla mnie).

PhiLho
źródło