W tym samouczku dowiesz się o zamknięciu w Pythonie, jak zdefiniować zamknięcie i dlaczego warto go używać.
Zmienna nielokalna w funkcji zagnieżdżonej
Zanim przejdziemy do tego, czym jest zamknięcie, musimy najpierw zrozumieć, czym jest funkcja zagnieżdżona i zmienna nielokalna.
Funkcja zdefiniowana wewnątrz innej funkcji nazywana jest funkcją zagnieżdżoną. Funkcje zagnieżdżone mogą uzyskiwać dostęp do zmiennych z otaczającego zakresu.
W Pythonie te zmienne nielokalne są domyślnie tylko do odczytu i musimy je jawnie zadeklarować jako nielokalne (używając słowa kluczowego nielokalnego), aby je zmodyfikować.
Poniżej znajduje się przykład zagnieżdżonej funkcji uzyskującej dostęp do zmiennej nielokalnej.
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) printer() # We execute the function # Output: Hello print_msg("Hello")
Wynik
cześć
Widzimy, że printer()
funkcja zagnieżdżona była w stanie uzyskać dostęp do nielokalnej zmiennej msg funkcji otaczającej.
Definiowanie funkcji zamknięcia
W powyższym przykładzie, co by się stało, gdyby ostatni wiersz funkcji print_msg()
zwrócił printer()
funkcję zamiast ją wywołać? Oznacza to, że funkcja została zdefiniowana następująco:
def print_msg(msg): # This is the outer enclosing function def printer(): # This is the nested function print(msg) return printer # returns the nested function # Now let's try calling this function. # Output: Hello another = print_msg("Hello") another()
Wynik
cześć
To niespotykane.
print_msg()
Funkcja została wywołana z łańcucha "Hello"
i funkcja zwrócony był związany z inną nazwą. Podczas wywoływania another()
wiadomość była nadal zapamiętywana, chociaż zakończyliśmy już wykonywanie print_msg()
funkcji.
Ta technika, za pomocą której niektóre dane ( "Hello
w tym przypadku) są dołączane do kodu, w Pythonie nazywa się zamknięciem .
Ta wartość w otaczającym zakresie jest zapamiętywana nawet wtedy, gdy zmienna wykracza poza zakres lub sama funkcja zostanie usunięta z bieżącej przestrzeni nazw.
Spróbuj uruchomić następujące polecenie w powłoce Pythona, aby zobaczyć dane wyjściowe.
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined
Tutaj zwrócona funkcja nadal działa, nawet jeśli oryginalna funkcja została usunięta.
Kiedy mamy zamknięcia?
Jak widać z powyższego przykładu, w Pythonie mamy zamknięcie, gdy funkcja zagnieżdżona odwołuje się do wartości w swoim zakresie obejmującym.
Kryteria, które należy spełnić, aby utworzyć domknięcie w Pythonie, podsumowano w poniższych punktach.
- Musimy mieć zagnieżdżoną funkcję (funkcję wewnątrz funkcji).
- Zagnieżdżona funkcja musi odnosić się do wartości zdefiniowanej w funkcji otaczającej.
- Funkcja otaczająca musi zwracać funkcję zagnieżdżoną.
Kiedy używać zamknięć?
Więc do czego służą zamknięcia?
Zamknięcia pozwalają uniknąć stosowania wartości globalnych i zapewniają pewną formę ukrywania danych. Może również zapewnić zorientowane obiektowo rozwiązanie problemu.
Kiedy istnieje kilka metod (w większości przypadków jedna metoda) do zaimplementowania w klasie, zamknięcia mogą stanowić alternatywne i bardziej eleganckie rozwiązanie. Ale gdy liczba atrybutów i metod rośnie, lepiej jest zaimplementować klasę.
Oto prosty przykład, w którym zamknięcie może być lepsze niż definiowanie klasy i tworzenie obiektów. Ale preferencje należą do Ciebie.
def make_multiplier_of(n): def multiplier(x): return x * n return multiplier # Multiplier of 3 times3 = make_multiplier_of(3) # Multiplier of 5 times5 = make_multiplier_of(5) # Output: 27 print(times3(9)) # Output: 15 print(times5(3)) # Output: 30 print(times5(times3(2)))
Wynik
27 15 30
Dekoratory Pythona również szeroko wykorzystują zamknięcia.
Na zakończenie warto zaznaczyć, że można znaleźć wartości, które są zawarte w funkcji domknięcia.
Wszystkie obiekty funkcji mają __closure__
atrybut, który zwraca krotkę obiektów komórek, jeśli jest to funkcja zamykająca. Nawiązując do powyższego przykładu, znamy times3
i times5
jesteśmy funkcjami domknięcia.
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)
Obiekt komórki ma atrybut cell_contents, który przechowuje zamkniętą wartość.
>>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5