Generierung von Funktionen innerhalb der Schleife mit Lambda-Ausdruck in Python

Wenn ich zwei Listen von Funktionen mache:

def makeFun(i): return lambda: i a = [makeFun(i) for i in range(10)] b = [lambda: i for i in range(10)] 

Warum sind die Listen a und b nicht gleich?

Beispielsweise:

 >>> a[2]() 2 >>> b[2]() 9 

6 Solutions collect form web for “Generierung von Funktionen innerhalb der Schleife mit Lambda-Ausdruck in Python”

Technisch gesehen ist der Lambda-Ausdruck über dem i , der im globalen Bereich sichtbar ist, der zuletzt auf 9 gesetzt ist, geschlossen. Es ist dasselbe, auf den i in allen 10 Lambdas verwiesen werde. Beispielsweise,

 i = 13 print b[3]() 

In der makeFun Funktion schließt das Lambda auf dem i , das definiert ist, wenn die Funktion aufgerufen wird. Das sind zehn verschiedene i s.

Wie andere schon gesagt haben, ist das Scoping das Problem. Beachten Sie, dass Sie dies lösen können, indem Sie dem Lambda-Ausdruck ein zusätzliches Argument hinzufügen und ihm einen Standardwert zuweisen:

 >> def makeFun(i): return lambda: i ... >>> a = [makeFun(i) for i in range(10)] >>> b = [lambda: i for i in range(10)] >>> c = [lambda i=i: i for i in range(10)] # <-- Observe the use of i=i >>> a[2](), b[2](), c[2]() (2, 9, 2) 

Das Ergebnis ist, dass i jetzt explizit in einen Bereich gesetzt wird, der auf den lambda Ausdruck beschränkt ist.

Ein Satz von Funktionen (a) arbeitet auf dem Argument und das andere (b) arbeitet auf einer globalen Variablen, die dann auf 9 gesetzt wird. Überprüfen Sie die Demontage:

 >>> import dis >>> dis.dis(a[2]) 1 0 LOAD_DEREF 0 (i) 3 RETURN_VALUE >>> dis.dis(b[2]) 1 0 LOAD_GLOBAL 0 (i) 3 RETURN_VALUE >>> 

Um etwas Klarheit hinzuzufügen (zumindest in meinem Kopf)

 def makeFun(i): return lambda: i a = [makeFun(i) for i in range(10)] b = [lambda: i for i in range(10)] 

A verwendet makeFun (i) was eine Funktion mit einem Argument ist.

B verwendet lambda: i was eine Funktion ohne Argumente ist. Das, was ich benutze, ist sehr verschieden von dem vorherigen

Um a und b gleich zu machen, können wir beide Funktionen ohne Argumente verwenden:

 def makeFun(): return lambda: i a = [makeFun() for i in range(10)] b = [lambda: i for i in range(10)] 

Jetzt verwenden beide Funktionen das globale i

 >>> a[2]() 9 >>> b[2]() 9 >>> i=13 >>> a[2]() 13 >>> b[2]() 13 

Oder (nützlicher) machen beide ein Argument:

 def makeFun(x): return lambda: x a = [makeFun(i) for i in range(10)] b = [lambda x=i: x for i in range(10)] 

Ich habe bewusst i mit x geändert, wo die Variable lokal ist. Jetzt:

 >>> a[2]() 2 >>> b[2]() 2 

Erfolg!

Lambdas in Python teilen den variablen Umfang, in dem sie erstellt werden. In Ihrem ersten Fall ist der Umfang der Lambda makeFun's. In Ihrem zweiten Fall ist es das globale i , das ist 9, weil es ein übriggebliebenes aus der Schleife ist.

Das verstehe ich trotzdem

Schöner Fang. Die Lambda in der Liste Verständnis ist das gleiche lokale i jedes Mal zu sehen.

Sie können es wie folgt umschreiben:

 a = [] for i in range(10): a.append(makefun(i)) b = [] for i in range(10): b.append(lambda: i) 

Mit dem gleichen Ergebnis.

  • Scoping in Python 'für' Schleifen
  • Warum ist eine Klassenvariable nicht im Listenverständnis definiert, sondern eine andere ist?
  • Python: Wie erfasse ich eine Variable, die in einem nicht globalen Ahnenumfang angegeben ist?
  • Python überschreibt Variablen in verschachtelten Funktionen
  • String mit 'f' Präfix in python-3.6
  • Warum können Funktionen in Python-Druckvariablen im Umschließungsbereich funktionieren, können sie aber nicht in der Zuordnung verwenden?
  • Wie kann ich auf eine Klassenvariable in einem Initialisierer in Python zugreifen?
  • Python-Funktion Anrufe sind Blutungen Umfang, stateful, nicht zu initialisieren Parameter?
  • Mit einer Variablen in einem Versuch, Fang, endgültige Aussage, ohne es draußen zu erklären
  • Funktion Ortsname Bindung aus einem äußeren Bereich
  • Den Block der Befehle, die in der mit Anweisung ausgeführt werden sollen
  • Python ist die beste Programmiersprache der Welt.