Ostatnio zacząłem grać z Pythonem i natrafiłem na coś dziwnego w sposobie działania zamknięć. Rozważ następujący kod:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Buduje prostą tablicę funkcji, które pobierają pojedyncze dane wejściowe i zwracają dane dodane przez liczbę. Funkcje są zbudowane w for
pętli, w której iterator i
biegnie od 0
do 3
. Dla każdej z tych liczb lambda
tworzona jest funkcja, która przechwytuje i
i dodaje ją do danych wejściowych funkcji. Ostatnia linia wywołuje drugą lambda
funkcję z 3
parametrem. Ku mojemu zaskoczeniu wynik był 6
.
Spodziewałem się 4
. Moje rozumowanie brzmiało: w Pythonie wszystko jest przedmiotem, a zatem każda zmienna jest niezbędnym wskaźnikiem do niej. Podczas tworzenia lambda
zamknięć i
, spodziewałem się, że zapisze wskaźnik do obiektu liczb całkowitych, na który wskazuje aktualnie i
. Oznacza to, że i
przypisany nowy obiekt liczby całkowitej nie powinien wpływać na wcześniej utworzone zamknięcia. Niestety, sprawdzenie adders
tablicy w debuggerze pokazuje, że tak. Wszystkie lambda
funkcje znajdują się w ostatniej wartości i
, 3
, co powoduje adders[1](3)
powrót 6
.
Zastanawiam się co do następujących rzeczy:
- Co dokładnie rejestrują zamknięcia?
- Jaki jest najbardziej elegancki sposób przekonania
lambda
funkcji do przechwytywania bieżącej wartościi
w sposób, który nie ulegnie zmianie poi
zmianie jej wartości?
i
opuszcza przestrzeń nazw?
print i
że nie będzie działać po pętli. Ale przetestowałem to dla siebie i teraz rozumiem, co masz na myśli - to działa. Nie miałem pojęcia, że zmienne pętli pozostały po pytle w ciele pętli.
if
, with
, try
itd.