Python wenn nicht == vs wenn! =

183

Was ist der Unterschied zwischen diesen beiden Codezeilen:

if not x == 'val':

und

if x != 'val':

Ist einer effizienter als der andere?

Wäre es besser zu benutzen

if x == 'val':
    pass
else:
Lafferc
quelle
101
Je besser das ist, das Sie lesen können, ich bezweifle, dass der Engpass Ihres Programms hier sein wird
Thomas Ayoub
1
Diese Frage interessiert mich im Fall "x nicht in Liste" und "nicht x in Liste"
SomethingSomething
5
@SomethingSomething werden sie identisch interpretiert.
Jonrsharpe
4
@SomethingSomething Referenz für meinen obigen Kommentar: stackoverflow.com/q/8738388/3001761
jonrsharpe
1
@SomethingSomething ist es auch für diese; So wird die Syntax interpretiert, es spielt keine Rolle, was die beiden Operanden sind.
Jonrsharpe

Antworten:

229

Verwenden Sie dis, um den für die beiden Versionen generierten Bytecode anzuzeigen:

not ==

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT           
             10 RETURN_VALUE   

!=

  4           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 RETURN_VALUE   

Letzteres hat weniger Operationen und ist daher wahrscheinlich etwas effizienter.


Es wurde darauf hingewiesen , in den commments (danke, @Quincunx ) , dass , wo man if foo != bargegen if not foo == bardie Anzahl der Operationen ist genau das gleiche, es ist nur , dass die COMPARE_OPÄnderungen und POP_JUMP_IF_TRUEschaltet auf POP_JUMP_IF_FALSE:

not ==::

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_TRUE        16

!=

  2           0 LOAD_FAST                0 (foo)
              3 LOAD_FAST                1 (bar)
              6 COMPARE_OP               3 (!=)
              9 POP_JUMP_IF_FALSE       16

In diesem Fall ist es unwahrscheinlich, dass Sie überhaupt einen Leistungsunterschied feststellen, es sei denn, es gab einen Unterschied im Arbeitsaufwand für jeden Vergleich.


Beachten Sie jedoch, dass die beiden Versionen nicht immer logisch identisch sind , da dies von den Implementierungen __eq__und __ne__für die betreffenden Objekte abhängt . Gemäß der Datenmodelldokumentation :

Es gibt keine impliziten Beziehungen zwischen den Vergleichsoperatoren. Die Wahrheit von x==ybedeutet nicht, dass dies x!=yfalsch ist.

Beispielsweise:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

Schließlich, und vielleicht am wichtigsten ist : in der Regel, wo die beiden sind logisch identisch, x != yist viel besser lesbar alsnot x == y .

Jonrsharpe
quelle
29
In der Praxis ist jede Klasse, die __eq__nicht mit übereinstimmt __ne__, pleite.
Kevin
8
Bitte beachten Sie, dass es nicht immer wahr ist, dass not x == yeine weitere Anweisung vorhanden ist. Als ich den Code in eine einfügte if, stellte sich heraus, dass beide die gleiche Anzahl von Anweisungen haben, nur eine POP_JUMP_IF_TRUEund die andere POP_JUMP_IF_FALSE(das war der einzige Unterschied zwischen ihnen, abgesehen von der Verwendung einer anderen COMPARE_OP). Als ich den Code ohne das ifs kompiliert habe , habe ich bekommen, was Sie haben.
Justin
1
Ein weiteres Beispiel , wo ==und schließen !=sich nicht aus ist eine SQL-ähnliche Umsetzung beteiligtnull Werte. In SQL nullkehrt nicht truezu im !=Vergleich zu jedem anderen Wert, so Python - Implementierungen von SQL - Schnittstellen auch das gleiche Problem haben.
Joe
Ich fange an zu wünschen, ich hätte nicht auf den möglichen Unterschied zwischen not ==und hingewiesen !=, es scheint der interessanteste Teil meiner Antwort zu sein! Ich denke nicht, dass dies der Ort ist, an dem man darüber nachdenken kann, ob, warum und wann dies sinnvoll ist - siehe z Warum hat Python eine __ne__Operatormethode anstelle von nur __eq__?
Jonrsharpe
29

@jonrsharpe hat eine ausgezeichnete Erklärung, was los ist. Ich dachte, ich würde nur den Zeitunterschied zeigen, wenn ich jede der 3 Optionen 10.000.000 Mal ausführe (genug, um einen kleinen Unterschied zu zeigen).

Verwendeter Code:

def a(x):
    if x != 'val':
        pass


def b(x):
    if not x == 'val':
        pass


def c(x):
    if x == 'val':
        pass
    else:
        pass


x = 1
for i in range(10000000):
    a(x)
    b(x)
    c(x)

Und der cProfile-Profiler ergibt:

Geben Sie hier die Bildbeschreibung ein

Wir können also sehen, dass zwischen if not x == 'val':und ein sehr kleiner Unterschied von ~ 0,7% besteht if x != 'val':. Von diesen if x != 'val':ist die schnellste.

Am überraschendsten ist jedoch, dass wir das sehen können

if x == 'val':
        pass
    else:

ist in der Tat die schnellste und schlägt if x != 'val':um ~ 0,3%. Dies ist nicht sehr gut lesbar, aber ich denke, wenn Sie eine vernachlässigbare Leistungsverbesserung wünschen, könnte man diesen Weg gehen.

Rotverschiebung
quelle
31
Ich hoffe , dass jeder weiß , nicht zu handeln , auf diesen Informationen! Unlesbare Änderungen für eine Verbesserung um 0,3% - oder sogar um 10% - vorzunehmen, ist selten eine gute Idee, und diese Art der Verbesserung ist sehr wahrscheinlich abklingend (und nicht in guter Weise : sehr geringfügige Änderungen in der Python-Laufzeit) könnte jeden Gewinn eliminieren oder sogar umkehren.
Malvolio
1
@ Malvolio Darüber hinaus gibt es verschiedene Implementierungen von Python.
Cees Timmerman
6

In der ersten muss Python eine Operation mehr ausführen als nötig (anstatt nur zu prüfen, ob sie nicht gleich ist, muss geprüft werden, ob sie nicht wahr ist, also eine weitere Operation). Es wäre unmöglich, den Unterschied zu einer Ausführung zu erkennen, aber wenn sie mehrmals ausgeführt würde, wäre die zweite effizienter. Insgesamt würde ich die zweite verwenden, aber mathematisch sind sie gleich

JediPythonClone
quelle
5
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               2 (==)
              9 UNARY_NOT
             10 POP_TOP
             11 LOAD_CONST               2 (None)
             14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
  1           0 LOAD_CONST               0 (10)
              3 LOAD_CONST               1 (20)
              6 COMPARE_OP               3 (!=)
              9 POP_TOP
             10 LOAD_CONST               2 (None)
             13 RETURN_VALUE

Hier können Sie sehen, dass not x == yeine Anweisung mehr als hat x != y. Daher ist der Leistungsunterschied in den meisten Fällen sehr gering, es sei denn, Sie führen Millionen von Vergleichen durch, und selbst dann ist dies wahrscheinlich nicht die Ursache für einen Engpass.

kylie.a
quelle
5

Ein zusätzlicher Hinweis, da die anderen Antworten Ihre Frage größtenteils richtig beantwortet haben, ist, dass wenn eine Klasse nur definiert __eq__()und nicht definiert __ne__(), Sie COMPARE_OP (!=)sie ausführen __eq__()und negieren. Zu diesem Zeitpunkt ist Ihre dritte Option wahrscheinlich ein kleines bisschen effizienter, sollte aber nur in Betracht gezogen werden, wenn Sie die Geschwindigkeit benötigen, da es schwierig ist, sie schnell zu verstehen.

Jacob Zimmerman
quelle
3

Es geht um deine Art, es zu lesen. notDer Operator ist dynamisch, deshalb können Sie ihn in anwenden

if not x == 'val':

Könnte !=aber in einem besseren Kontext als Operator gelesen werden, der das Gegenteil von dem ==tut, was er tut.

Himanshu Mishra
quelle
3
Was meinst du mit " notOperator ist dynamisch" ?
Jonrsharpe
1
@jonrsharpe Ich denke, er meint, dass "nicht x" x .__ bool __ () aufruft [Python 3 - Python 2 verwendet einen Wert ungleich Null ] und das Ergebnis zurücksetzt (siehe docs.python.org/3/reference/datamodel.html#object). __bool__ )
jdferreira
1

Ich möchte meinen obigen Lesbarkeitskommentar erweitern.

Auch hier stimme ich der Lesbarkeit voll und ganz zu, die andere (leistungsschwache) Bedenken außer Kraft setzt.

Ich möchte darauf hinweisen, dass das Gehirn "positiv" schneller interpretiert als "negativ". ZB "Stop" vs. "Nicht gehen" (ein ziemlich mieses Beispiel aufgrund der unterschiedlichen Anzahl von Wörtern).

Also eine Wahl gegeben:

if a == b
    (do this)
else
    (do that)

ist dem funktional äquivalenten vorzuziehen:

if a != b
    (do that)
else
    (do this)

Weniger Lesbarkeit / Verständlichkeit führt zu mehr Fehlern. Vielleicht nicht in der Erstcodierung, aber die (nicht so kluge wie Sie!) Wartungsänderungen ...

Alan Jay Weiner
quelle
1
Gehirn interpretiert "positiv" schneller als "negativ" ist dies aus Erfahrung oder haben Sie Studien darüber gelesen? Ich frage nur, weil ich je nach Code in (mach das) oder (mach das) a! = B leichter zu verstehen finde.
Lafferc