In Python, una chiusura è un'istanza di una funzione a cui sono associate variabili immutabilmente.
In effetti, il modello di dati lo spiega nella sua descrizione dell'attributo delle funzioni __closure__
:
Nessuno o una tupla di celle che contengono associazioni per le variabili libere della funzione. Sola lettura
Per dimostrarlo:
def enclosure(foo):
def closure(bar):
print(foo, bar)
return closure
closure_instance = enclosure('foo')
Chiaramente, sappiamo che ora abbiamo una funzione puntata dal nome della variabile closure_instance
. Apparentemente, se lo chiamiamo con un oggetto, bar
dovrebbe stampare la stringa 'foo'
e qualunque sia la rappresentazione della stringa bar
.
Infatti la stringa 'foo' è vincolata all'istanza della funzione, e possiamo leggerla direttamente qui, accedendo cell_contents
all'attributo della prima (e unica) cella nella tupla __closure__
dell'attributo:
>>> closure_instance.__closure__[0].cell_contents
'foo'
Per inciso, gli oggetti cella sono descritti nella documentazione dell'API C:
Gli oggetti "cella" vengono utilizzati per implementare le variabili a cui fanno riferimento più ambiti
E possiamo dimostrare l'utilizzo della nostra chiusura, notando che 'foo'
è bloccata nella funzione e non cambia:
>>> closure_instance('bar')
foo bar
>>> closure_instance('baz')
foo baz
>>> closure_instance('quux')
foo quux
E niente può cambiarlo:
>>> closure_instance.__closure__ = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
Funzioni parziali
L'esempio fornito utilizza la chiusura come funzione parziale, ma se questo è il nostro unico obiettivo, lo stesso obiettivo può essere raggiunto functools.partial
>>> from __future__ import print_function
>>> partial_function = functools.partial(print, 'foo')
>>> partial_function('bar')
foo bar
>>> partial_function('baz')
foo baz
>>> partial_function('quux')
foo quux
Ci sono anche chiusure più complicate che non si adattano all'esempio della funzione parziale e le dimostrerò ulteriormente quando il tempo lo consente.
nonlocal
stato aggiunto in python 3, python 2.x non aveva chiusure di lettura / scrittura complete (cioè si poteva leggere chiuse sulle variabili, ma non modificare i loro valori)