Warum ist in Python "0, 0 == (0, 0)" gleich "(0, False)"?

118

In Python (ich habe nur mit Python 3.6 geprüft, aber ich glaube, dass dies auch für viele der vorherigen Versionen gelten sollte):

(0, 0) == 0, 0   # results in a two element tuple: (False, 0)
0, 0 == (0, 0)   # results in a two element tuple: (0, False)
(0, 0) == (0, 0) # results in a boolean True

Aber:

a = 0, 0
b = (0, 0)
a == b # results in a boolean True

Warum unterscheidet sich das Ergebnis zwischen den beiden Ansätzen? Behandelt der Gleichheitsoperator Tupel anders?

Piotr Zakrzewski
quelle

Antworten:

156

Die ersten beiden Ausdrücke werden beide als Tupel analysiert:

  1. (0, 0) == 0(was ist False), gefolgt von0
  2. 0, gefolgt von 0 == (0, 0)(was immer noch Falseso ist).

Die Ausdrücke werden auf diese Weise aufgrund der relativen Priorität des Komma-Trennzeichens im Vergleich zum Gleichheitsoperator aufgeteilt: Python sieht ein Tupel mit zwei Ausdrücken, von denen einer zufällig ein Gleichheitstest ist, anstelle eines Gleichheitstests zwischen zwei Tupeln.

Aber in Ihrem zweiten Satz von Aussagen a = 0, 0 kann kein Tupel sein. Ein Tupel ist eine Sammlung von Werten, und im Gegensatz zu einem Gleichheitstest hat die Zuweisung in Python keinen Wert. Eine Aufgabe ist kein Ausdruck, sondern eine Aussage; Es hat keinen Wert, der in ein Tupel oder einen anderen umgebenden Ausdruck aufgenommen werden kann. Wenn Sie so etwas versuchen, (a = 0), 0um die Interpretation als Tupel zu erzwingen, wird ein Syntaxfehler angezeigt. Damit bleibt die Zuordnung eines Tupels zu einer Variablen - die durch Schreiben expliziter gemacht werden könnte a = (0, 0)- als einzig gültige Interpretation von a = 0, 0.

Also auch ohne die Klammern auf der Zuordnung zu a, sowohl es als auch bbekommen den Wert zugewiesen (0,0), so a == bist es daher True.

Mark Reed
quelle
17
Ich würde sagen, dass der Kommaoperator eine niedrigere Priorität als die Gleichheit hat, da die Bewertung der Gleichheit der des Kommaoperators vorausgeht: Gleichheit hat eine höhere Priorität als der Kommaoperator. Dies ist jedoch immer eine Quelle der Verwirrung. Ich wollte nur darauf hinweisen, dass andere Quellen Dinge umdrehen könnten.
Tomsmeding
2
Sie können die Verwirrung der unteren / höheren Redewendungen vermeiden, indem Sie stattdessen sagen, dass die ,Bindung weniger eng ist als ==.
Amalloy
4
Das Komma ist kein Operator docs.python.org/3.4/faq/…
Chris_Rands
48
Die Dokumente können behaupten, dass sie alles wollen, aber es spielt keine Rolle. Sie können einen Parser schreiben, damit jeder Operator seine eigene Produktion erhält und es nirgendwo in der Implementierung einen expliziten "Vorrang" gibt. Dies verhindert jedoch nicht, dass diese syntaktischen Einheiten Operatoren sind. Sie können "Operator" auf implementierungsspezifische Weise neu definieren , was sie anscheinend in Python getan haben, aber das ändert nichts an der Implikation des Begriffs. Das Komma ist effektiv ein Operator, der Tupel erzeugt. Seine Funktionsweise zeigt zum Beispiel, wie seine relative Priorität durch Klammern beeinflusst wird.
Mark Reed
68

Was Sie in allen drei Fällen sehen, ist eine Folge der Grammatikspezifikation der Sprache und der Art und Weise, wie im Quellcode gefundene Token analysiert werden, um den Analysebaum zu generieren.

Ein Blick auf diesen Low-Level-Code soll Ihnen helfen, zu verstehen, was unter der Haube passiert. Wir können diese Python-Anweisungen nehmen, sie in Bytecode konvertieren und sie dann mit dem disModul dekompilieren :

Fall 1: (0, 0) == 0, 0

>>> dis.dis(compile("(0, 0) == 0, 0", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               0 (0)
              6 COMPARE_OP               2 (==)
              9 LOAD_CONST               0 (0)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

(0, 0)wird zuerst mit zuerst verglichen 0und mit bewertet False. Ein Tupel wird dann mit diesem Ergebnis und zuletzt erstellt 0, so dass Sie erhalten (False, 0).

Fall 2: 0, 0 == (0, 0)

>>> dis.dis(compile("0, 0 == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               0 (0)
              3 LOAD_CONST               0 (0)
              6 LOAD_CONST               2 ((0, 0))
              9 COMPARE_OP               2 (==)
             12 BUILD_TUPLE              2
             15 POP_TOP
             16 LOAD_CONST               1 (None)
             19 RETURN_VALUE

Ein Tupel wird mit 0als erstem Element konstruiert . Für das zweite Element wird die gleiche Prüfung wie im ersten Fall durchgeführt und ausgewertet False, so dass Sie erhalten (0, False).

Fall 3: (0, 0) == (0, 0)

>>> dis.dis(compile("(0, 0) == (0, 0)", '', 'exec'))
  1           0 LOAD_CONST               2 ((0, 0))
              3 LOAD_CONST               3 ((0, 0))
              6 COMPARE_OP               2 (==)
              9 POP_TOP
             10 LOAD_CONST               1 (None)
             13 RETURN_VALUE

Wie Sie sehen, vergleichen Sie hier nur diese beiden (0, 0)Tupel und kehren zurück True.

cs95
quelle
20

Eine andere Möglichkeit, das Problem zu erklären: Sie sind wahrscheinlich mit Wörterbuchliteralen vertraut

{ "a": 1, "b": 2, "c": 3 }

und Array-Literale

[ "a", "b", "c" ]

und Tupelliterale

( 1, 2, 3 )

Was Sie jedoch nicht erkennen, ist, dass im Gegensatz zu Wörterbuch- und Array-Literalen die Klammern, die Sie normalerweise um ein Tupelliteral herum sehen, nicht Teil der Literal-Syntax sind . Die Literal-Syntax für Tupel ist nur eine Folge von Ausdrücken, die durch Kommas getrennt sind:

1, 2, 3

(eine "exprlist" in der Sprache der formalen Grammatik für Python ).

Was erwarten Sie nun vom Array-Literal?

[ 0, 0 == (0, 0) ]

zu bewerten? Das sieht wahrscheinlich viel mehr , wie es soll das gleiche sein wie

[ 0, (0 == (0, 0)) ]

was natürlich zu bewertet [0, False]. Ebenso mit einem explizit in Klammern gesetzten Tupelliteral

( 0, 0 == (0, 0) )

es ist nicht überraschend zu bekommen (0, False). Die Klammern sind jedoch optional.

0, 0 == (0, 0)

ist das gleiche. Und deshalb bekommst du (0, False).


Wenn Sie sich fragen, warum die Klammern um ein Tupelliteral optional sind, liegt dies hauptsächlich daran, dass es ärgerlich wäre, Destrukturierungszuweisungen auf diese Weise schreiben zu müssen:

(a, b) = (c, d) # meh
a, b = c, d     # better
zwol
quelle
17

Wenn Sie der Reihenfolge, in der Aktionen ausgeführt werden, einige Klammern hinzufügen, können Sie die Ergebnisse möglicherweise besser verstehen:

# Build two element tuple comprising of 
# (0, 0) == 0 result and 0
>>> ((0, 0) == 0), 0
(False, 0)

# Build two element tuple comprising of
# 0 and result of (0, 0) == 0 
>>> 0, (0 == (0, 0))
(0, False)

# Create two tuples with elements (0, 0) 
# and compare them
>>> (0, 0) == (0, 0) 
True

Das Komma wird verwendet, um Ausdrücke zu trennen (mit Klammern können wir natürlich ein anderes Verhalten erzwingen). Wenn Sie die aufgelisteten Snippets anzeigen, ,wird sie durch Komma getrennt und definiert, welche Ausdrücke ausgewertet werden:

(0, 0) == 0 ,   0
#-----------|------
  expr 1      expr2

Auf (0, 0)ähnliche Weise kann das Tupel auch zerlegt werden. Das Komma trennt zwei Ausdrücke, die aus den Literalen bestehen 0.

Dimitris Fasarakis Hilliard
quelle
6

Im ersten macht Python ein Tupel aus zwei Dingen:

  1. Der Ausdruck (0, 0) == 0, der zu ausgewertet wirdFalse
  2. Die Konstante 0

Im zweiten ist es umgekehrt.

irgendwie
quelle
0

Schauen Sie sich dieses Beispiel an:

r = [1,0,1,0,1,1,0,0,0,1]
print(r==0,0,r,1,0)
print(r==r,0,1,0,1,0)

dann Ergebnis:

False 0 [1, 0, 1, 0, 1, 1, 0, 0, 0, 1] 1 0
True 0 1 0 1 0

dann erfolgt der Vergleich nur mit der ersten Zahl (0 und r) im Beispiel.

Emad Saeidi
quelle