Kürzlich habe ich angefangen, mit Python herumzuspielen und bin dabei auf etwas Seltsames in der Funktionsweise von Closures gestoßen. Betrachten Sie den folgenden Code:
adders=[None, None, None, None]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Es bildet eine einfache Reihe von Funktionen, die eine einzelne Eingabe annehmen und diese um eine Zahl erhöht zurückgeben. Die Funktionen werden konstruiert in for
Schleife, in der der Iterator i
läuft von 0
a 3
. Für jede dieser Zahlen a lambda
wird eine Funktion erstellt, die Folgendes erfasst i
und fügt es der Eingabe der Funktion hinzu. Die letzte Zeile ruft die zweite lambda
Funktion mit 3
als Parameter. Zu meiner Überraschung war die Ausgabe 6
.
Ich hatte eine 4
. Meine Überlegung war: In Python ist alles ein Objekt und somit ist jede Variable im Wesentlichen ein Zeiger auf dieses Objekt. Beim Erstellen des lambda
Verschlüsse für i
erwartet, dass er einen Zeiger auf das Integer-Objekt speichert, auf das derzeit durch i
. Das bedeutet, dass, wenn i
ein neues Integer-Objekt zugewiesen wird, sollte dies keine Auswirkungen auf die zuvor erstellten Verschlüsse haben. Leider ist die Inspektion der adders
Array in einem Debugger zeigt, dass dies der Fall ist. Alle lambda
Funktionen beziehen sich auf den letzten Wert von i
, 3
was zu folgenden Ergebnissen führt adders[1](3)
zurückkehrend 6
.
Das wirft bei mir folgende Fragen auf:
- Was genau wird mit den Verschlüssen erfasst?
- Was ist der eleganteste Weg, um die
lambda
Funktionen zur Erfassung des aktuellen Wertes voni
in einer Weise, die nicht beeinträchtigt wird, wenni
seinen Wert ändert?