Zatvaranja Pythona: Kako ga koristiti i zašto?

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 ( "Hellou 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 times3i times5jesu 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

Zanimljivi članci...