W Pythonie zamknięcie (closure) to funkcja, która „zapamiętuje” wartości zewnętrznych zmiennych, nawet jeśli jest wywoływana poza ich zasięgiem. Zamknięcia są tworzone, gdy wewnętrzna funkcja odnosi się do zmiennych zdefiniowanych w funkcji zewnętrznej. Aby zmieniać takie zmienne z wewnętrznej funkcji w Pythonie 3, używa się słowa kluczowego nonlocal.
Przykład:
def make_accumulator(): total = 0 def add(value): nonlocal total total += value return total return add acc = make_accumulator() print(acc(10)) # 10 print(acc(5)) # 15
Bez nonlocal zmienna total byłaby niedostępna do zmiany (błąd UnboundLocalError).
Pytanie: „Czy można zwiększyć zmienną z funkcji zewnętrznej z wewnętrznej funkcji bez słowa kluczowego nonlocal?”
Odpowiedź: Nie, bez nonlocal spowoduje to błąd, ponieważ interpreter potraktuje zmienną jako lokalną dla wewnętrznej funkcji. Przykład:
def outer(): count = 0 def inner(): count += 1 # Błąd UnboundLocalError return count return inner
Historia W jednej aplikacji internetowej chciano zaimplementować licznik lokalnie dla każdego obsługiwacza przez funkcje zagnieżdżone, jednak zapomniano użyć
nonlocal. Licznik zawsze zwracał 1, ponieważ zliczał count w lokalnej przestrzeni i przyrastał nowy, niezwiązany z zewnętrznym zamknięciem.
Historia W zadaniach na równoległość (multithreading) często używa się zamknięć i lambd. Bez zrozumienia zamknięć powstała sytuacja, w której wszystkie funkcje lambda „zapamiętywały” ostatnią wartość zmiennej z pętli, a zadania kopiowały się nawzajem, zamiast przetwarzać różne zadania.
Historia Podczas pisania dekoratora bez użycia poprawnego zamknięcia (nie przekazano parametrów do zewnętrznego zasięgu) powstała sytuacja, w której dekorator nie mógł kumulować stanu między wywołaniami, co doprowadziło do nieoczekiwanych rezultatów i niestabilnego działania aplikacji.