Das Verständnis hat einige unerwartete Wechselwirkungen mit dem Umfang. Ist das das erwartete Verhalten?
Ich habe eine Methode:
def leave_room(self, uid):
u = self.user_by_id(uid)
r = self.rooms[u.rid]
other_uids = [ouid for ouid in r.users_by_id.keys() if ouid != u.uid]
other_us = [self.user_by_id(uid) for uid in other_uids]
r.remove_user(uid) # OOPS! uid has been re-bound by the list comprehension above
# Interestingly, it's rebound to the last uid in the list, so the error only shows
# up when len > 1
Dies ist eine brutale Fehlerquelle. Während ich neuen Code schreibe, finde ich nur gelegentlich sehr seltsame Fehler aufgrund des erneuten Bindens - selbst jetzt, wo ich weiß, dass es ein Problem ist. Ich muss eine Regel wie "Temp-Vars in Listenverständnissen immer mit Unterstrich voranstellen" festlegen, aber selbst das ist nicht narrensicher.
Die Tatsache, dass diese zufällige Zeitbombe wartet, negiert die nette "Benutzerfreundlichkeit" des Listenverständnisses.
python
binding
list-comprehension
Jabavu Adams
quelle
quelle
for
Konstruktfor
-loop und den Leckvariablen -loops entspricht . Es war also nicht explizit, sondern wurde implizit angegeben.Antworten:
Listenverständnisse verlieren die Regelungsvariable in Python 2, aber nicht in Python 3. Hier ist Guido van Rossum (Schöpfer von Python) , der die Geschichte dahinter erklärt:
quelle
break
- aber für Verständnisse irrelevant. Ich erinnere mich an einige comp.lang.python-Diskussionen, bei denen Leute Variablen mitten im Ausdruck zuweisen wollten. Der weniger verrückte Weg war der Einzelwert für Klauseln, z.sum100 = [s for s in [0] for i in range(1, 101) for s in [s + i]][-1]
, benötigt aber nur eine verständnislokale Variable und funktioniert genauso gut in Python 3. Ich denke, "undicht" war die einzige Möglichkeit, eine Variable außerhalb eines Ausdrucks sichtbar zu machen. Alle waren sich einig, dass diese Techniken schrecklich sind :-)Ja, Listenverständnisse "lecken" ihre Variable in Python 2.x, genau wie bei Schleifen.
Rückblickend wurde dies als Fehler erkannt und mit Generatorausdrücken vermieden. BEARBEITEN: Wie Matt B. bemerkt , wurde dies auch vermieden, wenn Syntaxen für das Festlegen und das Wörterbuchverständnis von Python 3 zurückportiert wurden.
Das Verhalten des Listenverständnisses musste wie in Python 2 belassen werden, ist jedoch in Python 3 vollständig behoben.
Dies bedeutet, dass in allen:
Das
x
ist immer lokal für den Ausdruck, während diese:In Python 2.x verlieren alle die
x
Variable in den umgebenden Bereich.UPDATE für Python 3.8 (?) : PEP 572 führt einen
:=
Zuweisungsoperator ein, der absichtlich aus dem Verständnis und den Generatorausdrücken herausläuft! Es ist im Wesentlichen durch zwei Anwendungsfälle motiviert: Erfassen eines "Zeugen" aus früh beendeten Funktionen wieany()
undall()
:und Aktualisieren des veränderlichen Zustands:
Siehe Anhang B für den genauen Umfang. Die Variable wird in der nächsten Umgebung
def
oder zugewiesenlambda
, es sei denn, diese Funktion deklariert sienonlocal
oderglobal
.quelle
Ja, die Zuweisung erfolgt dort wie in einer
for
Schleife. Es wird kein neuer Bereich erstellt.Dies ist definitiv das erwartete Verhalten: In jedem Zyklus ist der Wert an den von Ihnen angegebenen Namen gebunden. Zum Beispiel,
Sobald dies erkannt wurde, scheint es leicht zu vermeiden zu sein: Verwenden Sie keine vorhandenen Namen für die Variablen innerhalb des Verständnisses.
quelle
Interessanterweise hat dies keinen Einfluss auf das Wörterbuch oder das Verständnis.
Es wurde jedoch wie oben erwähnt in 3 behoben.
quelle
Einige Problemumgehungen für Python 2.6, wenn dieses Verhalten nicht erwünscht ist
quelle
In Python3 wird die Variable während des Listenverständnisses nicht geändert, nachdem der Gültigkeitsbereich überschritten wurde. Wenn wir jedoch eine einfache for-Schleife verwenden, wird die Variable außerhalb des Gültigkeitsbereichs neu zugewiesen.
i = 1 print (i) print ([i im Bereich (5)]) print (i) Der Wert von i bleibt nur 1.
Verwenden Sie jetzt einfach die for-Schleife. Der Wert von i wird neu zugewiesen.
quelle