Der folgende Code spuckt 1
zweimal aus, aber ich erwarte zu sehen 0
und dann 1
.
def pv(v) :
print v
x = []
for v in range(2):
x.append(lambda : pv(v))
for xx in x:
xx()
Ich hatte erwartet, dass Python-Lambdas hinter den Kulissen an die Referenz binden, auf die eine lokale Variable zeigt. Dies scheint jedoch nicht der Fall zu sein. Ich bin auf dieses Problem in einem großen System gestoßen, in dem das Lambda das moderne C ++ - Äquivalent einer Bindung ausführt (z. B. 'boost :: bind'), in dem Sie in einem solchen Fall an ein intelligentes ptr binden oder eine Kopie erstellen würden, um eine Kopie für das Lambda zu erstellen.
Wie binde ich eine lokale Variable an eine Lambda-Funktion und lasse sie bei Verwendung die richtige Referenz beibehalten? Ich bin ziemlich begeistert von dem Verhalten, da ich dies von einer Sprache mit einem Müllsammler nicht erwarten würde.
NameError
if auslösen, wenn sie außerhalb der Schleife aufgerufen werden. In den meisten anderen Sprachen sind Schleifenvariablen nur innerhalb der Schleife selbst zugänglich.Antworten:
Wechseln Sie
x.append(lambda : pv(v))
zux.append(lambda v=v: pv(v))
.Sie erwarten, dass "Python-Lambdas an die Referenz binden, auf die eine lokale Variable hinter den Kulissen zeigt", aber so funktioniert Python nicht. Python sucht den Variablennamen zum Zeitpunkt des Aufrufs der Funktion und nicht zum Zeitpunkt der Erstellung. Die Verwendung eines Standardarguments funktioniert, da Standardargumente beim Erstellen der Funktion ausgewertet werden und nicht beim Aufrufen.
Das ist nichts Besonderes an Lambdas. Erwägen:
x = "before foo defined" def foo(): print x x = "after foo was defined" foo()
druckt
quelle
functools.partial
oder Verwendung einer separaten Hilfsfunktion zum Erstellen der Verschlüsse (def make_closure(v): return lambda: pv(v)
in die Sie sie einfügen könnentest
).lambda par1, par2 = l3_e : self.parse_l4(par1, par2)
?v
selbst eine Referenz war (im Gegensatz zu einerint
), dann nehme ich an, dass es keine "tiefe" Kopie geben wird?Der Abschluss des Lambda enthält einen Verweis auf die verwendete Variable und nicht auf ihren Wert. Wenn sich also der Wert der Variablen später ändert, ändert sich auch der Wert im Abschluss. Das heißt, der Wert der Abschlussvariablen wird beim Aufruf der Funktion aufgelöst, nicht beim Erstellen. (Pythons Verhalten hier ist in der Welt der funktionalen Programmierung nicht ungewöhnlich, was es wert ist.)
Es gibt zwei Lösungen:
Verwenden Sie ein Standardargument, das den aktuellen Wert der Variablen zum Zeitpunkt der Definition an einen lokalen Namen bindet.
lambda v=v: pv(v)
Verwenden Sie ein Doppel-Lambda und rufen Sie sofort das erste an.
(lambda v: lambda: pv(v))(v)
quelle