Le celle di chiusura si riferiscono ai valori necessari per la funzione ma vengono ricavate dall'ambito circostante.
Quando Python compila una funzione nidificata, annota tutte le variabili a cui fa riferimento ma sono definite solo in una funzione padre (non globale) negli oggetti codice per la funzione nidificata e l'ambito genitore. Questi sono gli attributi co_freevars
e co_cellvars
sugli oggetti __code__
di queste funzioni, rispettivamente.
Quindi, quando in realtà creare la funzione nidificata (che si verifica quando viene eseguita la funzione padre), tali riferimenti vengono quindi utilizzati per collegare una chiusura alla funzione nidificata.
Una chiusura di funzione contiene una tupla di celle, una per ciascuna variabile libera (denominata in co_freevars
); le celle sono riferimenti speciali alle variabili locali di un ambito genitore, che seguono i valori a cui puntano le variabili locali. Questo è meglio illustrato con un esempio:
def foo():
def bar():
print(spam)
spam = 'ham'
bar()
spam = 'eggs'
bar()
return bar
b = foo()
b()
Nell'esempio precedente, la funzione bar
ha una cella di chiusura, che indica spam
nella funzione foo
. La cella segue il valore di spam
. Ancora più importante, una volta che foo()
viene completato e viene restituito bar
, la cella continua a fare riferimento al valore (la stringa eggs
) anche se la variabile all'interno di foo
non esiste più.
Pertanto, le uscite di codice di cui sopra:
>>> b=foo()
ham
eggs
>>> b()
eggs
e b.__closure__[0].cell_contents
è 'eggs'
.
Si noti che la chiusura è dereferenziata quando bar()
viene chiamato; la chiusura non cattura il valore qui. Che fa la differenza quando si produce funzioni annidate (con lambda
espressioni o def
dichiarazioni) che fanno riferimento l'indice del ciclo:
def foo():
bar = []
for spam in ('ham', 'eggs', 'salad'):
bar.append(lambda: spam)
return bar
for bar in foo():
print bar()
È possibile che questo stamperà salad
tre volte di fila, perché tutti e tre lambda
funzioni riferimento alla variabile spam
, non il valore a cui è stato associato quando è stato creato l'oggetto funzione. Al termine del ciclo for
, spam
è stato associato a 'salad'
, quindi tutte e tre le chiusure verranno risolte in tale valore.