Warum gibt der Ausdruck 0 <0 == 0 in Python False zurück?


136

Beim Betrachten von Queue.py in Python 2.6 fand ich dieses Konstrukt, das ich etwas seltsam fand:

def full(self):
    """Return True if the queue is full, False otherwise
    (not reliable!)."""
    self.mutex.acquire()
    n = 0 < self.maxsize == self._qsize()
    self.mutex.release()
    return n

Wenn maxsize0 ist, ist die Warteschlange nie voll.

Meine Frage ist, wie es in diesem Fall funktioniert? Wie 0 < 0 == 0wird als falsch angesehen?

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

Ist 0 <True gleich False in Python?
Marino Šimić

3
@Marino Šimić: Aus dem zweiten Beispiel in der Frage des OP geht hervor >>> (0) < (0 == 0), dass dies eindeutig nicht der Fall ist.
Martineau

3
Ein Grund, warum Sie keinen Code wie n = 0 < self.maxsize == self._qsize()in irgendeiner Sprache schreiben sollten . Wenn Ihre Augen mehrmals über die Linie hin und her huschen müssen, um herauszufinden, was los ist, ist dies keine gut geschriebene Linie. Teilen Sie es einfach in mehrere Zeilen auf.
BlueRaja - Danny Pflughoeft

2
@Blue: Ich bin damit einverstanden, einen solchen Vergleich nicht auf diese Weise zu schreiben, aber die Aufteilung in separate Zeilen geht bei zwei Vergleichen etwas über Bord. Ich hoffe du meinst, teile es in separate Vergleiche auf. ;)
Jeff Mercado

2
@Blue: Ich habe es nicht geschrieben, es ist in Python 2.6. Ich habe nur versucht zu verstehen, was los war.
Marcelo Santos

Antworten:


113

Ich glaube, Python hat eine spezielle Fallbehandlung für Sequenzen von Vergleichsoperatoren, um Bereichsvergleiche einfach auszudrücken. Es ist viel schöner sagen zu können 0 < x <= 5als zu sagen (0 < x) and (x <= 5).

Diese werden als verkettete Vergleiche bezeichnet . Und das ist ein Link zur Dokumentation für sie.

In den anderen Fällen, über die Sie sprechen, wird in Klammern ein Vergleichsoperator vor dem anderen angewendet, sodass es sich nicht mehr um verkettete Vergleiche handelt. Und da Trueund FalseWerte als Ganzzahlen haben, erhalten Sie die Antworten, die Sie tun, aus den in Klammern gesetzten Versionen.


Es ist interessant, einige dieser Vergleiche auszuprobieren und int () und bool () anzugeben. Ich erkannte, dass der bool () einer Nicht-Null 1 ist. Ich denke, ich hätte vor diesem Gedankenexperiment niemals versucht, etwas anderes als bool (0) oder bool (1) direkt anzugeben
j_syk

Wollten Sie stattdessen auf diesen Abschnitt verlinken? docs.python.org/2/reference/expressions.html#comparisons
Tavnab

@tavnab - Ja. Ich werde versuchen, mich daran zu erinnern, es zu beheben. Ich werde auch den Bearbeitungsverlauf überprüfen. Das scheint kein Fehler zu sein, den ich machen würde. 😕
Omnifarious

42

weil

(0 < 0) and (0 == 0)

ist False. Sie können Vergleichsoperatoren verketten und sie werden automatisch zu paarweisen Vergleichen erweitert.


EDIT - Klarstellung über Richtig und Falsch in Python

In Python Trueund Falsesind nur Instanzen von bool, was eine Unterklasse von ist int. Mit anderen Worten, ist Truewirklich nur 1.

Der Punkt dabei ist, dass Sie das Ergebnis eines booleschen Vergleichs genau wie eine Ganzzahl verwenden können. Dies führt zu verwirrenden Dingen wie

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

Dies geschieht jedoch nur, wenn Sie die Vergleiche in Klammern setzen, damit sie zuerst ausgewertet werden. Andernfalls erweitert Python die Vergleichsoperatoren.


2
Ich habe gestern eine interessante Verwendung für boolesche Werte gesehen, die als Ganzzahlen verwendet werden. Der Ausdruck 'success' if result_code == 0 else 'failure'kann wie folgt umgeschrieben werden ('error', 'success')[result_code == 0]: Ich hatte noch nie einen Booleschen Wert gesehen, mit dem ein Element in einer Liste / einem Tupel ausgewählt wurde.
Andrew Clark

'bool' wurde irgendwann um Python 2.2 hinzugefügt.
MRAB

18

Das seltsame Verhalten, das Sie erleben, beruht auf der Fähigkeit von Python, Bedingungen zu verketten. Da festgestellt wird, dass 0 nicht kleiner als 0 ist, entscheidet es, dass der gesamte Ausdruck als falsch ausgewertet wird. Sobald Sie dies in separate Bedingungen zerlegen, ändern Sie die Funktionalität. Es testet zunächst im Wesentlichen das a < b && b == cfür Ihre ursprüngliche Aussage von a < b == c.

Ein anderes Beispiel:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

1
OMG, a < b && b == cist das gleiche wie a < b == cOO
Kiril Kirov

9
>>> 0 < 0 == 0
False

Dies ist ein verketteter Vergleich. Es gibt true zurück, wenn jeder paarweise Vergleich der Reihe nach true ist. Es ist das Äquivalent zu(0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

Dies entspricht 0 < Trueder Auswertung von True.

>>> (0 < 0) == 0
True

Dies entspricht False == 0der Auswertung von True.

>>> 0 < (0 == 0)
True

Entspricht 0 < Truedem, wie oben, True.


7

Mit Blick auf die Demontage (die Bytes - Codes) ist es offensichtlich, warum 0 < 0 == 0ist False.

Hier ist eine Analyse dieses Ausdrucks:

>>>import dis

>>>def f():
...    0 < 0 == 0

>>>dis.dis(f)
  2      0 LOAD_CONST               1 (0)
         3 LOAD_CONST               1 (0)
         6 DUP_TOP
         7 ROT_THREE
         8 COMPARE_OP               0 (<)
        11 JUMP_IF_FALSE_OR_POP    23
        14 LOAD_CONST               1 (0)
        17 COMPARE_OP               2 (==)
        20 JUMP_FORWARD             2 (to 25)
   >>   23 ROT_TWO
        24 POP_TOP
   >>   25 POP_TOP
        26 LOAD_CONST               0 (None)
        29 RETURN_VALUE

Hinweiszeilen 0-8: Diese Zeilen prüfen, ob 0 < 0diese offensichtlich Falseauf den Python-Stapel zurückkehren.

Beachten Sie nun Zeile 11: JUMP_IF_FALSE_OR_POP 23 Dies bedeutet, dass Sie bei 0 < 0Rückgabe Falseeinen Sprung zu Zeile 23 ausführen.

Jetzt 0 < 0wird Falseder Sprung ausgeführt, der den Stapel mit einem FalseRückgabewert für den gesamten Ausdruck verlässt 0 < 0 == 0, obwohl der == 0Teil nicht einmal überprüft ist.

Abschließend ist die Antwort wie in anderen Antworten auf diese Frage angegeben. 0 < 0 == 0hat eine besondere Bedeutung. Der Compiler wertet dies mit zwei Begriffen aus: 0 < 0und 0 == 0. Wie bei allen komplexen booleschen Ausdrücken andzwischen ihnen wird der zweite nicht einmal überprüft, wenn der erste fehlschlägt.

Ich hoffe, dass dies die Dinge ein wenig aufklärt, und ich hoffe wirklich, dass die Methode, mit der ich dieses unerwartete Verhalten analysiert habe, andere dazu ermutigen wird, dies in Zukunft auch zu versuchen.


Wäre es nicht einfacher, es aus der Spezifikation herauszuarbeiten, als eine bestimmte Implementierung rückzuentwickeln?
David Heffernan

Nein, das ist die kurze Antwort. Ich glaube, dass es von Ihrer Persönlichkeit abhängt. Wenn Sie sich auf eine "Black Box" -Ansicht beziehen und Ihre Antworten lieber aus Spezifikationen und Dokumentationen beziehen möchten, wird Sie diese Antwort nur verwirren. Wenn Sie gerne die Interna von Dingen entdecken möchten, dann ist diese Antwort für Sie. Ihre Bemerkung, dass dieses Reverse Engineering nur für eine bestimmte Implementierung relevant ist, ist richtig und muss hervorgehoben werden, aber das war nicht der Punkt dieser Antwort. Es ist eine Demonstration, wie einfach es in Python ist, einen Blick "unter die Haube" für diejenigen zu werfen, die neugierig genug sind.
SatA

1
Das Problem beim Reverse Engineering ist der Mangel an Vorhersagekraft. Es ist nicht der Weg, eine neue Sprache zu lernen.
David Heffernan

Eine andere Sache, die ich hinzufügen muss, ist, dass Spezifikationen und Dokumentationen nicht immer vollständig sind und in den meisten Fällen keine Antworten für solche speziellen Fälle liefern. Dann, glaube ich, darf man keine Angst haben, so viel zu erforschen, zu untersuchen und tiefer zu gehen, wie es nötig ist, um die Antworten zu erhalten.
SatA

2

Wie bereits erwähnt, x comparison_operator y comparison_operator zhandelt es sich um syntaktischen Zucker (x comparison_operator y) and (y comparison_operator z)mit dem Bonus, dass y nur einmal bewertet wird.

Ihr Ausdruck 0 < 0 == 0ist also wirklich (0 < 0) and (0 == 0), was zu False and Truewelchem ​​gerecht wird False.


2

Vielleicht kann dieser Auszug aus den Dokumenten helfen:

Dies sind die sogenannten "Rich-Compare" -Methoden, die für Vergleichsoperatoren bevorzugt __cmp__()werden. Die Korrespondenz zwischen Operatorsymbole und Methodennamen ist wie folgt: x<yAnrufe x.__lt__(y), x<=yAnrufe x.__le__(y), x==yAnrufe x.__eq__(y), x!=yund x<>y Anruf x.__ne__(y), x>yAnrufe x.__gt__(y)und x>=yAnrufe x.__ge__(y).

Eine umfangreiche Vergleichsmethode kann den Singleton zurückgeben, NotImplementedwenn die Operation für ein bestimmtes Argumentpaar nicht implementiert wird. Konventionell Falseund Truewerden für einen erfolgreichen Vergleich zurückgegeben. Diese Methoden können jedoch einen beliebigen Wert zurückgeben. Wenn der Vergleichsoperator in einem booleschen Kontext verwendet wird (z. B. unter der Bedingung einer if-Anweisung), ruft Python bool()den Wert auf, um festzustellen, ob das Ergebnis wahr oder falsch ist.

Es gibt keine impliziten Beziehungen zwischen den Vergleichsoperatoren. Die Wahrheit von x==ybedeutet nicht, dass dies x!=y falsch ist. Dementsprechend sollte beim Definieren __eq__()auch definiert werden __ne__(), dass sich die Operatoren wie erwartet verhalten. Im folgenden Abschnitt finden Sie __hash__()einige wichtige Hinweise zum Erstellen von Hash-Objekten, die benutzerdefinierte Vergleichsvorgänge unterstützen und als Wörterbuchschlüssel verwendet werden können.

Es gibt keine Versionen dieser Methoden mit vertauschten Argumenten (die verwendet werden sollen, wenn das linke Argument die Operation nicht unterstützt, das rechte Argument jedoch). vielmehr __lt__()und __gt__() sind die Reflexion des anderen __le__() und __ge__()sind die Reflexion des anderen und __eq__()und __ne__() sind ihre eigene Reflexion.

Argumente für reichhaltige Vergleichsmethoden werden niemals erzwungen.

Dies waren Vergleiche, aber da Sie Vergleiche verketten , sollten Sie Folgendes wissen:

Vergleiche können willkürlich verkettet werden, z. B. x < y <= zist äquivalent zu x < y and y <= z, außer dass y nur einmal ausgewertet wird (aber in beiden Fällen wird z überhaupt nicht ausgewertet, wenn x <y als falsch befunden wird).

Wenn a, b, c, ..., y, z Ausdrücke sind und op1, op2, ..., opN Vergleichsoperatoren sind, dann ist a op1 b op2 c ... y opN z äquivalent zu op1 b und b op2 c und ... y opN z, außer dass jeder Ausdruck höchstens einmal ausgewertet wird.


1

Hier ist es in seiner ganzen Pracht.

>>> class showme(object):
...   def __init__(self, name, value):
...     self.name, self.value = name, value
...   def __repr__(self):
...     return "<showme %s:%s>" % (self.name, self.value)
...   def __cmp__(self, other):
...     print "cmp(%r, %r)" % (self, other)
...     if type(other) == showme:
...       return cmp(self.value, other.value)
...     else:
...       return cmp(self.value, other)
... 
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>> 

0

Ich denke, Python macht es komisch zwischen Magie. Gleich wie 1 < 2 < 3Mittel 2 zwischen 1 und 3 liegt.

In diesem Fall denke ich, dass [mittlere 0] größer als [links 0] und gleich [rechts 0] ist. Die mittlere 0 ist nicht größer als die linke 0, daher wird sie als falsch ausgewertet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.