Warum ist "if not someobj:" in Python besser als "if someobj == None:"?

127

Ich habe mehrere Beispiele für Code wie diesen gesehen:

if not someobj:
    #do something

Aber ich frage mich, warum nicht:

if someobj == None:
    #do something

Gibt es einen Unterschied? Hat einer einen Vorteil gegenüber dem anderen?

fuentesjr
quelle
13
Im Allgemeinen ist 'someobj is None' 'someobj == None' vorzuziehen
Aaron Maenpaa
Was ist mit Pseudocode, wenn X nicht None ist? Oder X != None?
Charlie Parker
Diese Frage ist ausführlicher als die anderen ... Lob ...
Alamin

Antworten:

187

Im ersten Test versucht Python, das Objekt in einen boolWert zu konvertieren, falls dies noch nicht geschehen ist. Grob fragen wir das Objekt: Sind Sie bedeutungsvoll oder nicht? Dies erfolgt mit dem folgenden Algorithmus:

  1. Wenn das Objekt über eine __nonzero__spezielle Methode verfügt (wie auch über numerische integrierte Funktionen intund float), wird diese Methode aufgerufen. Es muss entweder einen boolWert zurückgeben, der dann direkt verwendet wird, oder einen intWert, der berücksichtigt wird, Falsewenn er gleich Null ist.

  2. Andernfalls, wenn das Objekt eine hat __len__spezielle Methode (wie auch Behälter Einbauten, list, dict, set, tuple, ...), ist es diese Methode aufruft, einen Behälter unter Berücksichtigung , Falsewenn es leer ist (Länge ist null).

  3. Andernfalls wird das Objekt berücksichtigt, es Truesei denn, Nonein diesem Fall wird es berücksichtigt False.

Im zweiten Test wird das Objekt auf Gleichheit mit verglichen None. Hier fragen wir das Objekt: "Sind Sie diesem anderen Wert gleich?" Dies erfolgt mit dem folgenden Algorithmus:

  1. Wenn das Objekt eine __eq__Methode hat, wird es aufgerufen, und der Rückgabewert wird dann in einen boolWert konvertiert und verwendet, um das Ergebnis der zu bestimmen if.

  2. Andernfalls wird das Objekt __cmp__aufgerufen , wenn es über eine Methode verfügt. Diese Funktion muss eine Rückgabe intder Reihenfolge der beiden Objekte zurückgeben ( -1if self < other, 0if self == other, +1if self > other).

  3. Andernfalls wird das Objekt auf Identität verglichen (dh es bezieht sich auf dasselbe Objekt, wie vom isBediener getestet werden kann ).

Mit dem isBediener ist ein weiterer Test möglich . Wir würden das Objekt fragen: "Sind Sie dieses bestimmte Objekt?"

Im Allgemeinen würde ich empfehlen, den ersten Test mit nicht numerischen Werten zu verwenden, den Test auf Gleichheit zu verwenden, wenn Sie Objekte derselben Art (zwei Zeichenfolgen, zwei Zahlen, ...) vergleichen und nur dann auf Identität prüfen möchten Verwenden von Sentinel-Werten ( Nonedh nicht initialisiert für ein Mitgliedsfeld, zum Beispiel oder bei Verwendung der getattroder der __getitem__Methoden).

Zusammenfassend haben wir:

>>> 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
quelle
4
Obwohl technisch korrekt, erklärt dies nicht, dass Tupel, Listen, Dikte, Strs, Unicodes, Ints, Floats usw. ungleich Null sind . Es ist weitaus üblicher, sich auf den Wahrheitswert des eingebauten Typs zu verlassen, als sich auf eine benutzerdefinierte Methode ungleich Null zu verlassen .
ddaa
50

Dies sind eigentlich beide schlechte Praktiken. Es war einmal in Ordnung, None und False beiläufig als ähnlich zu behandeln. Seit Python 2.2 ist dies jedoch nicht die beste Richtlinie.

Wenn Sie einen if xoder eine if not xArt Test durchführen, muss Python zunächst implizit xin einen Booleschen Wert konvertieren . Die Regeln für die boolFunktion beschreiben eine Reihe von Dingen, die falsch sind; alles andere ist wahr. Wenn der Wert von x anfangs nicht richtig boolesch war, ist diese implizite Konvertierung nicht wirklich die klarste Art, Dinge zu sagen.

Vor Python 2.2 gab es keine Bool-Funktion, daher war es noch weniger klar.

Zweitens sollten Sie nicht wirklich mit testen == None. Sie sollten is Noneund verwenden is not None.

Siehe PEP 8, Style Guide für 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!

Wie viele Singletons gibt es? Five: None, True, False, NotImplementedund Ellipsis. Da es wirklich unwahrscheinlich ist, dass Sie NotImplementedoder verwenden Ellipsis, und Sie würden es niemals sagen if x is True(weil es einfach if xviel klarer ist), werden Sie immer nur testen None.

S.Lott
quelle
3
Die zweite Form ist kategorisch keine schlechte Praxis. PEP 8 empfiehlt die Verwendung von if x zweimal . Zuerst für Sequenzen (anstatt len ​​zu verwenden) und dann für True und False (anstatt is zu verwenden). Praktisch jeder Python-Code, den ich gesehen habe, verwendet if x und wenn nicht x.
Antti Rasinen
35

Denn es Noneist nicht das einzige, was als falsch angesehen wird.

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, (), [], {}Und ""sind alle unterschiedlich None, so dass Ihre beiden Code - Snippets sind nicht gleichwertig.

Beachten Sie außerdem Folgendes:

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

if object:ist keine Gleichstellungsprüfung. 0, (), [], None, {}, Usw. sind alle verschieden voneinander, aber sie alle zu bewerten auf False.

Dies ist die "Magie" hinter kurzschließenden Ausdrücken wie:

foo = bar and spam or eggs

Das ist eine Abkürzung für:

if bar:
    foo = spam
else:
    foo = eggs

obwohl du wirklich schreiben solltest:

foo = spam if bar else egg
badp
quelle
In Bezug auf Ihre letzte Frage sind sie gleichwertig.
Sylvain Defresne
Und beide falsch, weil "" falsch ist. Der zweite sollte '("",) oder ("s",)' lauten. Wie auch immer, moderne Versionen von Python haben einen richtigen ternären Operator. Dieser fehleranfällige Hack sollte verbannt werden.
ddaa
4

PEP 8 - Style Guide für Python Code empfiehlt die Verwendung von is oder is, wenn Sie auf None-ness testen

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

Wenn Sie jedoch auf mehr als None-ness testen, sollten Sie den booleschen Operator verwenden.

Vinko Vrsalovic
quelle
3

Wenn du fragst

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

die __nonzero__ Methode von Spam wird aufgerufen. Aus dem Python-Handbuch:

__nonzero__ ( self ) Wird aufgerufen, um das Testen von Wahrheitswerten und die integrierte Operation bool () zu implementieren. sollte False oder True oder ihre ganzzahligen Entsprechungen 0 oder 1 zurückgeben. Wenn diese Methode nicht definiert ist, wird __len __ () aufgerufen, wenn sie definiert ist (siehe unten). Wenn eine Klasse weder __len __ () noch __nonzero __ () definiert, werden alle ihre Instanzen als wahr betrachtet.

Wenn du fragst

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

die __eq__ Methode von Spam wird mit dem Argument genannt None .

Weitere Informationen zu den Anpassungsmöglichkeiten finden Sie in der Python-Dokumentation unter https://docs.python.org/reference/datamodel.html#basic-customization

Pi.
quelle
2

Diese beiden Vergleiche dienen unterschiedlichen Zwecken. Der erste prüft auf den booleschen Wert von etwas, der zweite prüft auf Identität mit dem Wert None.

zgoda
quelle
1
Um absolut korrekt zu sein, prüft die zweite auf Gleichheit mit dem Wert None ... "someobj is None" prüft auf Identität.
Matthew Trevor
0

Zum einen ist das erste Beispiel kürzer und sieht besser aus. Wie in den anderen Beiträgen, hängt Ihre Auswahl auch davon ab, was Sie wirklich mit dem Vergleich machen möchten.

rslite
quelle
0

Die Antwort lautet "es kommt darauf an".

Ich verwende das erste Beispiel, wenn ich 0, "", [] und False (Liste nicht erschöpfend) in diesem Zusammenhang als gleichwertig mit None betrachte.

Matthias Kestenholz
quelle
0

Persönlich habe ich einen konsistenten Ansatz für alle Sprachen gewählt: Ich mache if (var)(oder gleichwertig) nur, wenn var als boolesch deklariert ist (oder als solches definiert ist, haben wir in C keinen bestimmten Typ). Ich stelle diesen Variablen sogar ein b(so wäre es bVartatsächlich) voran, um sicherzugehen, dass ich hier nicht versehentlich einen anderen Typ verwende.
Ich mag implizites Casting nicht wirklich in Boolesche Werte, noch weniger, wenn es zahlreiche komplexe Regeln gibt.

Natürlich werden die Leute anderer Meinung sein. Einige gehen weiter, ich sehe if (bVar == true)im Java-Code bei meiner Arbeit (zu redundant für meinen Geschmack!), Andere lieben zu viel kompakte Syntax, gehen while (line = getNextLine())(zu mehrdeutig für mich).

PhiLho
quelle