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.

  • Warum ist eine Klassenvariable nicht im Listenverständnis definiert, sondern eine andere ist?
  • Lambda-Funktion acessing außerhalb variabel [duplicate]
  • Wie kann man Variablen eines anderen Bereichs ändern?
  • Python Variable Scope (Vorbehalt durch Verweis oder Kopie?)
  • Python überschreibt Variablen in verschachtelten Funktionen
  • Kurze Beschreibung der Scoping Regeln?
  • Den Block der Befehle, die in der mit Anweisung ausgeführt werden sollen
  • UnboundLocalError bei der Manipulation von Variablen ergibt inkonsistentes Verhalten
  • Globals und Einheimische in python exec ()
  • Zweifel über python variablen Bereich
  • String mit 'f' Präfix in python-3.6
  • Python ist die beste Programmiersprache der Welt.