Il termine "closure" è stato preso dalla programmazione funzionale ed è presente in Python sin dall'inizio. Le closures consentono a una funzione di ricordare l'ambiente in cui è stata creata, anche se vengono chiamate al di fuori di tale ambiente. Questo concetto fornisce flessibilità e consente di implementare molti schemi, comprese le fabbriche di funzioni e i calcoli pigri.
In Python, le funzioni sono oggetti di prima classe. A volte è necessario che una funzione interna utilizzi variabili dall'ambito della funzione esterna, anche dopo che quest'ultima è terminata. Il normale scoping lessicale non garantisce ciò al momento del ritorno della funzione. Se una funzione si riferisce alle variabili del suo ambiente di creazione, si verifica una closure.
Una closure si verifica se una funzione interna fa riferimento a variabili definite nell'esterno e quella esterna restituisce l'interna al di fuori. Questo è spesso utilizzato per creare fabbriche di funzioni, incapsulando lo stato senza classi e costruendo funzioni con parametri "in-loco".
def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30
nonlocal.Può una closure mantenere uno stato modificabile tra le chiamate, se la variabile viene modificata all'interno della funzione interna?
Sì, se all'interno della funzione interna viene utilizzata la parola chiave nonlocal. Senza nonlocal, l'assegnazione crea una nuova variabile locale, senza modificare l'esterno.
def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2
È possibile implementare variabili private in Python usando le closures invece delle classi?
Sì, le closure offrono una semplice implementazione di variabili "private" che non sono accessibili dall'esterno, a meno che non vengano forniti getter/setter nella funzione interna.
Le closures si applicano solo alle funzioni? È possibile organizzare una closure con le lambda in Python?
Sì, una closure può essere formata anche con le espressioni lambda poiché sono simili a def per il binding delle variabili lessicali.
def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8
nonlocal.Una fabbrica di funzioni che crea gestori in un ciclo utilizza una variabile di ciclo all'interno della closure:
handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]
Vantaggi:
Svantaggi:
i, il suo ultimo valore è 2 — comportamento inaspettato per la maggior parte.Utilizzato un argomento di default per "fissare" il valore:
handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]
Vantaggi:
Svantaggi: