2016-05-17 3 views
2

Wenn ich tun:Liste oder Generator von Lambda funcs kehren nicht das gleiche Ergebnis in Python

def create_funcs(): 
    return (lambda: i for i in range(5)) 

for f in create_funcs(): 
    print(f()) 

ich das erwartet:

1 
2 
3 
4 

Aber wenn ich tun:

def create_funcs(): 
    return [lambda: i for i in range(5)] 

for f in create_funcs(): 
    print(f()) 

Ich bekomme ein komisches:

4 
4 
4 
4 

Kann jemand erklären warum?

+0

@BrenBarn: Tut mir leid, ich verstehe nicht, wie die Antwort der anderen Frage bezieht sich auf meine. –

+0

Schauen Sie insbesondere auf [diese Antwort] (http://stackoverflow.com/questions/4575698/python-list-comprehension-overriding-value/4575866#4575866). – BrenBarn

+0

@BrenBarn: OK, aber ich bin auf Python3.5, also warum würde es lecken? –

Antwort

5

Ihre lambda Funktion erfasst eine Variable i aus dem umgebenden Bereich. Wie in this question beschrieben, ist dieser Umfang der Umfang des Verständnisses.

Ihre Schleife durchläuft nacheinander die Elemente des Generators und ruft jede einzelne vor dem Weiterschalten des Generators auf. Wenn Sie die erste Funktion erhalten, wird der Generator in einem Zustand angehalten, in dem i 0 ist, also das ist der Wert, den die Funktion sieht. Wenn Sie den Generator vorrücken, setzt er i auf 1, und Sie erhalten eine Funktion, die diesen Wert ergreift. Der Generator "wartet" auf i, bis Sie zur nächsten Funktion wechseln.

Im Listenverständnis erhalten Sie alle Funktionen auf einmal. Aber alle verweisen immer noch auf die gleiche Variable i innerhalb des Verständnisbereichs. Da das Listenverstehen den ganzen Weg bis zum Ende geht, geht i den ganzen Weg bis 4, bevor Sie die Möglichkeit haben, irgendeine der Funktionen zu benutzen.

In beiden Fällen referenziert Ihr Lambda eine Variable im Verständnisbereich. Es ist nur so, dass diese Variable im Generator-Verständnis nicht vorrückt, nachdem Sie die vorherige Funktion bereits aufgerufen haben. Sie können ein ähnliches Verhalten wie die Liste Verständnis Fall sehen, wenn Sie die erste Funktion speichern, aber nennen Sie es erst, nachdem Sie den Generator wieder vorrücken:

def create_funcs(): 
    return (lambda: i for i in range(5)) 

gen = create_funcs() 
f1 = next(gen) 
f2 = next(gen) 
print(f1()) 
print(f2()) 

Der Ausgang ist

1 
1 

Seit Ich erweiterte den Generator, i ist 1, also beide f1 und f2 sehen diesen Wert, wenn sie aufgerufen werden. Sie können die gleiche Sache sehen, wenn Sie for f in list(create_funcs()) tun und den Generator zwingen, den ganzen Weg zum Ende zu gehen, wie das Listenverstehen tut.

+0

Ich verstehe. Wenn ich 'lambda: i' durch' lambda: int (i) 'ersetze, funktioniert es, da ich nicht mehr direkt auf i referenziere. Danke für Ihre Geduld. –

+0

@JacquesGaudin: Die Verwendung von 'int (i)' sollte nichts ändern. – BrenBarn

+0

'Lambda: int (i)' sollte sich genauso verhalten wie Ihr aktueller Code. Wenn Sie das Problem umgehen möchten, verwenden Sie "Lambda i = i: i". Dies bindet den Wert von "i" zum Zeitpunkt der Lambda-Erstellung an einen Standardwert im eigenen Namensraum des Lambda. – Blckknght

-1

Wie BrenBarn bereits angedeutet hat, ist die Variable i im Listenverständnis lokal zum Umfang des Verständnisses. Dies funktioniert, wenn Sie Klammern verwenden:

def create_funcs(): 
    return (lambda: i for i in range(5)) 

for f in create_funcs(): 
    print(f()) 

0 
1 
2 
3 
4 
+1

Er macht das schon im ersten Beispiel in seiner Frage. Seine Frage ist, warum das Verhalten in den beiden Fällen unterschiedlich ist. – BrenBarn

Verwandte Themen