wenn A vs wenn A nicht ist Keine:

154

Kann ich benutzen:

if A:

anstatt

if A is not None:

Letzteres scheint so ausführlich. Ist da ein Unterschied?

rbairos
quelle

Antworten:

149

Die Aussage

if A:

ruft auf A.__nonzero__()(siehe Dokumentation zu speziellen Methodennamen ) und verwendet den Rückgabewert dieser Funktion. Hier ist die Zusammenfassung:

object.__nonzero__(self)

Wird aufgerufen, um Wahrheitswerttests und den eingebauten Betrieb zu implementieren bool(). sollte zurückgeben Falseoder oder Trueoder ihre ganzzahligen Äquivalente 0oder 1. Wenn diese Methode nicht definiert ist, __len__()wird sie aufgerufen, wenn sie definiert ist, und das Objekt wird als wahr betrachtet, wenn sein Ergebnis ungleich Null ist. Wenn eine Klasse weder __len__()noch definiert __nonzero__(), werden alle ihre Instanzen als wahr betrachtet.

Andererseits,

if A is not None:

vergleicht nur die Referenz Amit None, um festzustellen, ob sie gleich ist oder nicht.

Greg Hewgill
quelle
4
und so A is not Noneist schneller, da es viel weniger Arbeit zu tun gibt
John La Rooy
40
@gnibbler Nicht nach meinen Tests. if object(): passbeträgt ~ 0,130 usec pro Schleife, während if object() is not None: pass~ 0,135 usec beträgt. Auf jeden Fall sollten Sie nicht die Leistung verwenden, um zwischen diesen beiden zu wählen, sondern die Unterschiede in ihrer Funktionsweise betrachten, da sie nicht gleichwertig sind .
Lauritz V. Thaulow
1
@gnibbler if A is not Nonescheint langsamer zu sein, da es sich um einen Vergleich handelt und der eingebaute Singleton Noneals Zwischenschritt zum Vergleichen geladen werden muss A(siehe dis.dis()). Korrigieren Sie mich, wenn ich falsch if A:liege, aber effizienter zu sein scheint, sobald Sie wirklich den Wahrheitswert und nicht die NoneIdentität testen möchten.
Cedbeu
2
@cedbeu, Scheint vom Wert von A abzuhängen. Ich habe jetzt getestet, python -m timeit -s"a=0" "if a: pass" "else: pass"ist schneller als, python -m timeit -s"a=0" "if a is None: pass" "else: pass"aber python -m timeit -s"a=1" "if a: pass" "else: pass"langsamer. Könnte plattformabhängig sein, sehen Sie, ob Sie die gleichen Ergebnisse erhalten
John La Rooy
2
@cedbeu, in Python3 sind sie alle viel schneller, aber der is NoneTest war in der Tat der langsamste für mich. In Pypy haben sie alle genau das gleiche gemessen :)
John La Rooy
51

Wie in PEP8 geschrieben :

  • Vergleiche mit Singletons wie None sollten immer mit 'is' oder 'is not' durchgeführt werden, niemals mit den Gleichheitsoperatoren .

    Außerdem passen sie des Schreibens „ wenn x“ , wenn Sie wirklich bedeuten „ wenn x nicht None“ - zum Beispiel , wenn , ob eine Variable oder ein Argument zu testen , dass standardmäßig keine auf einen anderen Wert gesetzt wurde. Der andere Wert hat möglicherweise einen Typ (z. B. einen Container), der in einem booleschen Kontext falsch sein kann!

Scraplesh
quelle
7
Mmmh, das ist jedoch nicht sehr klar und reagiert nicht auf das OP. Die automatische Umleitung zum PEP ist nicht immer die gute Antwort. Sehr oft interessiert man sich nicht für den Identitätstest mit dem Singleton None, sondern nur für eine Wahrheitsprüfung. In diesem Fall if A:scheint es effizienter zu sein (nehmen Sie a dis.dis(), es gibt die zusätzlichen Schritte zum Laden des eingebauten Noneund Vergleichens mit if A is not None:, während es jump_ifim anderen Fall nur a gibt ).
Cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

so ist es nicht dasselbe wie

if x is not None # which works only on None
Abdul Muneer
quelle
17

Viele Funktionen geben None zurück, wenn keine geeigneten Ergebnisse vorliegen. Beispielsweise gibt die .first()Methode einer SQLAlchemy-Abfrage None zurück, wenn das Ergebnis keine Zeilen enthält. Angenommen, Sie haben einen Wert ausgewählt, der möglicherweise 0 zurückgibt, und müssen wissen, ob er tatsächlich 0 ist oder ob die Abfrage überhaupt keine Ergebnisse erzielt hat.

Eine gebräuchliche Redewendung besteht darin, dem optionalen Argument einer Funktion oder Methode den Standardwert None zu geben und dann zu testen, ob dieser Wert None ist, um festzustellen, ob er angegeben wurde. Beispielsweise:

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

Vergleiche das mit:

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

Was passiert in letzterem Fall, wenn Sie anrufen spam(0)oder spam([])? Die Funktion erkennt (fälschlicherweise), dass Sie keinen Wert für übergeben haben, eggsund berechnet einen Standardwert für Sie. Das ist wahrscheinlich nicht was du willst.

Oder stellen Sie sich eine Methode wie "Rückgabe der Liste der Transaktionen für ein bestimmtes Konto" vor. Wenn das Konto nicht vorhanden ist, wird möglicherweise None zurückgegeben. Dies unterscheidet sich von der Rückgabe einer leeren Liste (was bedeuten würde, dass "dieses Konto existiert, aber keine Transaktionen aufgezeichnet hat").

Zum Schluss zurück zum Datenbankmaterial. Es gibt einen großen Unterschied zwischen NULL und einer leeren Zeichenfolge. Eine leere Zeichenfolge sagt normalerweise "hier gibt es einen Wert, und dieser Wert ist überhaupt nichts". NULL sagt "Dieser Wert wurde nicht eingegeben."

In jedem dieser Fälle möchten Sie verwenden if A is None. Sie suchen nach einem bestimmten Wert - Keine - und nicht nur nach einem Wert, der zufällig in "Falsch" umgewandelt wird.

Kirk Strauser
quelle
10

Sie machen sehr unterschiedliche Dinge .

Die unten überprüft , ob A hat nichts außer den Werten False, [], None, ''und 0. Es überprüft den Wert von A.

if A:

Im Folgenden wird überprüft, ob A ein anderes Objekt als None ist. Es überprüft und vergleicht die Referenz (Speicheradresse) von A und None.

if A is not None:

UPDATE: Weitere Erklärung

Oft scheinen die beiden dasselbe zu tun, so dass viele Leute sie austauschbar verwenden - das ist eine wirklich schlechte Idee. Der Grund, warum die beiden die gleichen Ergebnisse liefern, ist oft ein reiner Zufall aufgrund von Optimierungen des Interpreters / Compilers wie Internierung oder etwas anderem.

Unter Berücksichtigung dieser Optimierungen verwenden Ganzzahlen und Zeichenfolgen mit demselben Wert denselben Speicherplatz. Das erklärt wahrscheinlich, warum zwei separate Zeichenfolgen so wirken, als wären sie gleich.

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

Andere Dinge verhalten sich jedoch nicht gleich.

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

Die beiden Listen haben eindeutig ein eigenes Gedächtnis. Überraschenderweise verhalten sich Tupel wie Strings.

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

Dies liegt wahrscheinlich daran, dass sich Tupel garantiert nicht ändern. Daher ist es sinnvoll, denselben Speicher wiederzuverwenden.

Fazit: Sie können sich nicht auf Zufälle verlassen . Nur weil es wie eine Ente quakt, heißt das nicht, dass es eine Ente ist. Verwenden Sie isund ==je nachdem, was Sie wirklich überprüfen möchten. Diese Dinge können schwer zu debuggen sein, da sie sich iswie eine Prosa lesen, die wir oft nur überfliegen.

Pithikos
quelle
Noneist ein Singleton, es ist kein Implementierungsdetail (im Gegensatz zu int oder string interning). Ich bin mir nicht sicher, ob ich dem folge, was du meinst.
Lev Levitsky
@ LevLevitsky Mein Punkt ist nicht, dass Nonesich das anders verhält als intoder straufgrund von Internierung. Mein Punkt ist das isund ==ich überprüfe verschiedene Dinge; überprüft zuerst eine Speicheradresse, die zweite überprüft den Inhalt von Speicheradressen.
Pithikos
@LevLevitsky Ich habe meine Antwort jetzt bearbeitet, um sie etwas verdaulicher zu machen.
Pithikos
7

if A: wird sich als falsch erweisen, wenn A 0, Falsch, leere Zeichenfolge, leere Liste oder Keine ist, was zu unerwünschten Ergebnissen führen kann.

Keflavich
quelle
1
Und viele andere Werte, wie leere Liste, leere Menge, leeres Tupel usw. Im Wesentlichen alles, was gemäß docs.python.org/3/library/stdtypes.html#truth-value-testing nicht wahr ist .
Jarmod
6

Die meisten Anleitungen, die ich gesehen habe, empfehlen, dass Sie verwenden sollten

wenn eine:

es sei denn, Sie haben einen Grund, genauer zu sein.

Es gibt einige geringfügige Unterschiede. Es gibt andere Werte als None, die False zurückgeben, z. B. leere Listen oder 0. Überlegen Sie sich also, worauf Sie wirklich testen.

Colin Coghill
quelle
5

None ist ein spezieller Wert in Python, der normalerweise eine nicht initialisierte Variable bezeichnet. Um zu testen, ob A diesen bestimmten Wert nicht hat, verwenden Sie:

if A is not None

Falsey-Werte sind eine spezielle Klasse von Objekten in Python (z. B. false, []). Um zu testen, ob A falsch ist, verwenden Sie:

if not A

Daher sind die beiden Ausdrücke nicht gleich. Und Sie sollten sie besser nicht als Synonyme behandeln.


PS Keine ist auch falsch, daher impliziert der erste Ausdruck den zweiten. Aber der zweite behandelt neben None noch andere Falsey-Werte. Nun ... wenn Sie sicher sein können, dass Sie in None keine anderen Falsey-Werte als None haben können, können Sie den ersten Ausdruck durch den zweiten ersetzen.

mircealungu
quelle
Es ist nicht so, dass Sie sich irren, aber diese Antwort deckt dies bereits ab.
Makoto
Boh, ich möchte Sie bitten, die Ablehnung zu entfernen, wenn Sie nichts dagegen haben. Meine Antwort ist mit allen anderen gleichwertig, vielleicht weniger mit der, auf die Sie sich beziehen, wird aber aus einer anderen Perspektive dargestellt, und es könnte für jemanden einfacher zu verstehen sein. Wenn ich eine Ablehnung von SO sehe, gehe ich außerdem von einer falschen Antwort aus ... was Sie zugeben, ist nicht der Fall.
mircealungu
Nun, diese Annahme ist unvollständig. Es könnte falsch oder einfach nicht nützlich sein. Da Ihre Antwort, wie gesagt, bereits behandelt wurde, bin ich nicht davon überzeugt, dass sie nützlich ist.
Makoto
4

Das hängt vom Kontext ab.

Ich verwende es, if A:wenn ich eine AArt Sammlung erwarte , und ich möchte den Block nur ausführen, wenn die Sammlung nicht leer ist. Auf diese Weise kann der Anrufer jede gut erzogene Sammlung übergeben, ob leer oder nicht, und sie das tun lassen, was ich erwarte. Es ermöglicht Noneund Falseunterdrückt auch die Ausführung des Blocks, was gelegentlich zum Aufrufen von Code geeignet ist.

OTOH, wenn ich erwarte , dass Aeinige völlig beliebiges Objekt sein , aber es Verzug geraten hätte zu None, dann habe ich immer verwenden if A is not None, als Code aufrufen absichtlich einen Verweis auf eine leere Sammlung, leeren String oder ein 0-wertigen numerischen Typ bestanden haben könnte, oder Boolescher Wert Falseoder eine Klasseninstanz, die im booleschen Kontext falsch ist.

Auf der anderen Seite, wenn ich erwarte A, dass es sich um eine spezifischere Sache handelt (z. B. eine Instanz einer Klasse, deren Methoden ich aufrufen werde), die jedoch Nonestandardmäßig verwendet werden könnte , und ich betrachte die standardmäßige boolesche Konvertierung als a Eigentum der Klasse Es macht mir nichts aus, alle Unterklassen durchzusetzen, dann erspare ich if A:meinen Fingern nur die schreckliche Last, zusätzliche 12 Zeichen einzugeben.

Ben
quelle
3

Ich habe eine Datei namens erstellt test.pyund sie auf dem Interpreter ausgeführt. Sie können ändern, was Sie möchten, um sicherzugehen, wie sich die Dinge hinter den Kulissen entwickeln.

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)

Dies ist der Assembler-Unterschied:

Quelle:

>>> 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'>
Benutzer
quelle
2

Ersteres ist mehr Pythonic (besserer ideomatischer Code), führt den Block jedoch nicht aus, wenn A False ist (nicht None).

Borealid
quelle
7
-1. Wie @klesh erwähnt, sagt PEP8 zu verwenden is/is not None. Nach PEP8 folgt Pythonic. Außerdem sind die beiden Tests unterschiedlich .
JCotton
1

Python> = 2,6,

wenn wir schreiben wie

if A:

generiert eine Warnung als,

FutureWarning: Das Verhalten dieser Methode wird sich in zukünftigen Versionen ändern. Verwenden Sie stattdessen einen bestimmten Test 'len (elem)' oder 'elem is not None'.

Also können wir verwenden

if A is not None:
normalUser
quelle