3.1 Wprowadzenie do zamknięć
Zamknięcie to funkcja, która przechwytuje zmienne z otaczającego ją zakresu widoczności, nawet po tym, jak ten zakres zakończył swoje działanie. To oznacza, że zamknięcie może „pamiętać” wartości zmiennych z zewnętrznego zakresu widoczności i nadal z nimi pracować, nawet gdy ten zakres już nie działa.
Aby zrozumieć, jak działają zamknięcia, rozważmy następujący przykład:
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(10)
print(closure(5)) # Wyjście: 15
Zobaczmy, co tu się dzieje:
Funkcja zewnętrzna (outer_function)
: Ta funkcja przyjmuje argument x
i definiuje wewnętrzną funkcję inner_function
, która przyjmuje argument y
i zwraca sumę x
i y
. Funkcja inner_function
nie jest wywoływana wewnątrz outer_function
, a jedynie deklarowana.
Funkcja wewnętrzna (inner_function)
: Ta funkcja jest zwracana z outer_function
i zachowuje wewnętrznie referencję do wartości x
, która została przekazana do outer_function
.
Zamknięcie: Zmienna closure
staje się zamknięciem, które „pamięta” wartość x
(w tym przypadku 10) i może używać jej przy wywołaniu.
Najczęściej przy pierwszym kontakcie z zamknięciami jest trudno je zrozumieć. Więc spróbujmy zwiększyć Twoje zrozumienie zamknięć na przykładach.
3.2 Przykłady użycia zamknięć
Tworzenie funkcji-generatora
Zamknięcia mogą być używane do tworzenia funkcji-generatorów, które generują sekwencje wartości.
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = make_counter()
print(counter()) # Wyjście: 1
print(counter()) # Wyjście: 2
print(counter()) # Wyjście: 3
Wyjaśnienie:
Funkcja-generator (make_counter)
: Ta funkcja tworzy zmienną count
i zwraca wewnętrzną funkcję counter
, która zwiększa wartość count
i ją zwraca.
Zamknięcie: Funkcja counter
zachowuje stan zmiennej count
i może ją zmieniać przy każdym wywołaniu.
Tworzenie funkcji z konfiguracją
Zamknięcia mogą być użyte do tworzenia funkcji z wstępnie zdefiniowaną konfiguracją.
def make_multiplier(factor):
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # Wyjście: 10
print(triple(5)) # Wyjście: 15
Wyjaśnienie:
Funkcja-konfigurator (make_multiplier)
: Ta funkcja przyjmuje mnożnik factor
i zwraca wewnętrzną funkcję multiplier
, która mnoży wejściową wartość przez factor
.
Zamknięcia: Funkcje double
i triple
są zamknięciami, które przechowują swoje własne wartości factor
i używają ich do mnożenia.
Filtrowanie danych z parametrami
Zamknięcia mogą być przydatne do tworzenia funkcji filtrujących z parametrami.
def make_filter(threshold):
def filter_func(value):
return value > threshold
return filter_func
filter_above_10 = make_filter(10)
data = [5, 10, 15, 20]
filtered_data = list(filter(filter_above_10, data))
print(filtered_data) # Wyjście: [15, 20]
Wyjaśnienie:
Funkcja-filtr (make_filter)
: Ta funkcja przyjmuje wartość progową threshold
i zwraca wewnętrzną funkcję filter_func
, która sprawdza, czy wartość przekracza próg.
Zamknięcie: Funkcja filter_func
przechowuje wartość threshold
i używa jej do filtrowania danych.
3.3 Plusy i minusy zamknięć
Zalety użycia zamknięć
Inkasulacja stanu: Zamknięcia pozwalają na inkapsulację stanu w funkcji, co pozwala uniknąć globalnych zmiennych i poprawia czytelność oraz stabilność kodu.
Elastyczność: Zamknięcia mogą być używane do tworzenia funkcji z pewną wymaganą konfiguracją lub zachowaniem, co czyni kod bardziej elastycznym i adaptacyjnym.
Programowanie funkcyjne: Zamknięcia są ważną koncepcją w programowaniu funkcyjnym, pozwalając na tworzenie funkcji wyższego rzędu i innych konstrukcji funkcyjnych.
Ograniczenia i potencjalne problemy
Mimo wielu zalet, zamknięcia mają też swoje ograniczenia:
Użycie pamięci: Zamknięcia mogą utrzymywać referencje do obiektów, które już nie są używane, co może prowadzić do wycieków pamięci.
Trudność w debugowaniu: Zamknięcia mogą skomplikować debugowanie kodu, ponieważ stan zmiennych może być nieoczywisty.
GO TO FULL VERSION