programowanieProgramista Python

Jak Python implementuje mechanizm pracy z zamkniętymi funkcjami (closures)? Do czego służą zamknięcia, jakie są pułapki i jak poprawnie zmieniać zmienne funkcji zewnętrznej?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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 z pułapką.

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

Przykłady prawdziwych błędów z powodu braku znajomości szczegółów tematu.


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.