U ovom vodiču naučit ćete o zatvaranju Pythona, kako definirati zatvaranje i razlozima zbog kojih biste ga trebali koristiti.
Nelokalna varijabla u ugniježđenoj funkciji
Prije nego što uđemo u to što je zatvaranje, prvo moramo shvatiti što je ugniježđena funkcija i nelokalna varijabla.
Funkcija definirana unutar druge funkcije naziva se ugniježđena funkcija. Ugniježđene funkcije mogu pristupiti varijablama obuhvaćenog opsega.
U Pythonu su ove ne-lokalne varijable prema zadanim postavkama samo za čitanje i moramo ih eksplicitno deklarirati kao ne-lokalne (koristeći nelokalnu ključnu riječ) kako bismo ih modificirali.
Slijedi primjer ugniježđene funkcije koja pristupa ne-lokalnoj varijabli.
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")
Izlaz
zdravo
Možemo vidjeti da je ugniježđena printer()
funkcija mogla pristupiti ne-lokalnoj varijabli msg funkcije koja obuhvaća.
Definiranje funkcije zatvaranja
U gornjem primjeru, što bi se dogodilo da posljednji redak funkcije print_msg()
vrati printer()
funkciju umjesto da je pozove? To znači da je funkcija definirana kako slijedi:
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()
Izlaz
zdravo
To je neobično.
print_msg()
Funkcija zvao s nizom "Hello"
i vratila funkcija je dužan na ime drugog. Na pozivu another()
se poruka još uvijek sjećala iako smo već završili s izvršavanjem print_msg()
funkcije.
Ova tehnika pomoću koje se neki podaci ( "Hello
u ovom slučaju) pridružuju kodu naziva se zatvaranje u Pythonu .
Ova vrijednost u obuhvaćajućem opsegu pamti se čak i kada varijabla izlazi iz opsega ili se sama funkcija ukloni iz trenutnog prostora imena.
Pokušajte pokrenuti sljedeće u Python ljusci da biste vidjeli izlaz.
>>> del print_msg >>> another() Hello >>> print_msg("Hello") Traceback (most recent call last):… NameError: name 'print_msg' is not defined
Ovdje vraćena funkcija i dalje radi čak i kad je izvorna funkcija izbrisana.
Kada imamo zatvaranja?
Kao što se vidi iz gornjeg primjera, imamo zatvaranje u Pythonu kada ugniježđena funkcija upućuje na vrijednost u svom opsegu zatvaranja.
Kriteriji koji moraju biti zadovoljeni za stvaranje zatvaranja u Pythonu sažeti su u sljedećim točkama.
- Moramo imati ugniježđenu funkciju (funkciju unutar funkcije).
- Ugniježđena funkcija mora se odnositi na vrijednost definiranu u priloženoj funkciji.
- Funkcija zatvaranja mora vratiti ugniježđenu funkciju.
Kada koristiti zatvarače?
Pa čemu služe zatvaranja?
Zatvaranjem se može izbjeći uporaba globalnih vrijednosti i pruža neki oblik skrivanja podataka. Također može pružiti objektno orijentirano rješenje problema.
Kada postoji nekoliko metoda (u većini slučajeva jedna metoda) koje treba primijeniti u klasi, zatvaranja mogu pružiti alternativno i elegantnije rješenje. Ali kad se broj atributa i metoda poveća, bolje je implementirati klasu.
Evo jednostavnog primjera gdje bi zatvaranje moglo biti poželjnije od definiranja klase i izrade objekata. Ali preferencija je tvoja.
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)))
Izlaz
27 15 30
Python Decorators također uvelike koriste i zatvarače.
Na kraju, dobro je istaknuti da se vrijednosti koje se zatvaraju u funkciji zatvaranja mogu saznati.
Svi funkcionalni objekti imaju __closure__
atribut koji vraća skup ćelijskih objekata ako je riječ o funkciji zatvaranja. Pozivajući se na gornji primjer, znamo times3
i times5
jesu funkcije zatvaranja.
>>> make_multiplier_of.__closure__ >>> times3.__closure__ (,)
Objekt ćelije ima atribut cell_contents koji pohranjuje zatvorenu vrijednost.
>>> times3.__closure__(0).cell_contents 3 >>> times5.__closure__(0).cell_contents 5