Di recente ho iniziato a giocare con Python e ho scoperto qualcosa di strano nel modo in cui funzionano le chiusure. Considera il seguente codice:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Costruisce un semplice array di funzioni che accettano un singolo input e restituiscono quell'input aggiunto da un numero. Le funzioni sono costruiti in for
ciclo in cui l'iteratore i
va da 0
a 3
. Per ciascuno di questi numeri lambda
viene creata una funzione che acquisisce i
e la aggiunge all'input della funzione. L'ultima riga chiama la seconda lambda
funzione con3
come parametro. Con mia sorpresa l'output è stato 6
.
Mi aspettavo a 4
. Il mio ragionamento era: in Python ogni cosa è un oggetto e quindi ogni variabile è essenziale un puntatore ad esso. Durante la creazione delle lambda
chiusure per i
, mi aspettavo che memorizzasse un puntatore all'oggetto intero attualmente indicato da i
. Ciò significa che quando i
assegnato un nuovo oggetto intero non dovrebbe influenzare le chiusure precedentemente create. Purtroppo, l'ispezione adders
dell'array all'interno di un debugger mostra che lo fa. Tutte le lambda
funzioni si riferiscono all'ultimo valore i
, 3
, che si traduce in adders[1](3)
ritorno 6
.
Il che mi fa chiedere quanto segue:
- Cosa catturano esattamente le chiusure?
- Qual è il modo più elegante per convincere le
lambda
funzioni a catturare il valore attualei
in un modo che non sarà influenzato quandoi
cambia il suo valore?
i
lascia lo spazio dei nomi?
print i
non avrebbe funzionato dopo il ciclo. Ma l'ho testato da solo e ora capisco cosa intendi: funziona. Non avevo idea che le variabili del loop indugiassero dopo il corpo del loop in Python.
if
, with
, try
ecc