Cosa acquisiscono le chiusure delle funzioni (lambda)?

| | | |

Recentemente ho iniziato a giocare con Python e ho scoperto qualcosa di peculiare nel modo in cui funzionano le chiusure. Considera il seguente codice:

adders=[None, None, None, None] for i in [0,1,2,3]: adders[i]=lambda a: i+a print sommatori[1](3) 

Costruisce un semplice array di funzioni che accettano un singolo input e restituiscono quell'input aggiunto da un numero. Le funzioni sono costruite nel ciclo for in cui l'iteratore i va da 0 a 3. Per ognuno di questi numeri viene creata una funzione lambda che cattura i e lo aggiunge all'input della funzione. L'ultima riga chiama il secondo lambda funzione con 3 come parametro. Con mia sorpresa, l'output è stato 6.

Mi aspettavo un 4. Il mio il ragionamento era: in Python tutto è un oggetto e quindi ogni variabile è essenziale un puntatore ad esso. Durante la creazione delle chiusure lambda per i, mi aspettavo che memorizzasse un puntatore a l'oggetto intero attualmente puntato da i. Ciò significa che quando i ha assegnato un nuovo oggetto intero, non dovrebbe avere effetto sulle chiusure precedentemente create. Purtroppo, l'ispezione dell'array adders all'interno di un debugger mostra che lo fa. Tutte le funzioni lambda fanno riferimento all'ultimo valore di i, 3, che risulta in adders[1](3) restituendo 6.

Il che mi fa riflettere su quanto segue:

  • Cosa catturano esattamente le chiusure?
  • Qual è il modo più elegante per convincere le funzioni lambda a catturare il valore corrente di i in un modo che non sarà influenzato quando i cambia il suo valore?