Python Decorators: Kako ga koristiti i zašto?

Dekorator uzima funkciju, dodaje neku funkcionalnost i vraća je. U ovom vodiču naučit ćete kako možete stvoriti dekorator i zašto biste ga trebali koristiti.

Dekoratori u Pythonu

Python ima zanimljivu značajku koja se naziva dekoratori za dodavanje funkcionalnosti postojećem kodu.

To se naziva i metaprogramiranjem, jer dio programa pokušava izmijeniti drugi dio programa u vrijeme sastavljanja.

Preduvjeti za učenje dekoratera

Da bismo razumjeli dekoratere, prvo moramo znati nekoliko osnovnih stvari u Pythonu.

Moramo biti ugodni s činjenicom da su sve u Pythonu (da! Čak i klase) objekti. Imena koja definiramo su jednostavno identifikatori vezani uz te objekte. Funkcije nisu iznimka, one su također objekti (s atributima). Na isti objekt funkcije mogu se vezati različita imena.

Evo primjera.

 def first(msg): print(msg) first("Hello") second = first second("Hello")

Izlaz

 bok bok

Kada pokrenete kod, obje funkcije firsti seconddaju isti rezultat. Evo, imena firsti secondodnose se na istu funkciju objekta.

Sad stvari postaju čudnije.

Funkcije se mogu proslijediti kao argumenti drugoj funkciji.

Ako ste koristili funkcije kao što su map, filteri reduceu Python, onda već znate o tome.

Takve funkcije koje uzimaju druge funkcije kao argumente nazivaju se i funkcijama višeg reda . Evo primjera takve funkcije.

 def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result

Funkciju pozivamo na sljedeći način.

 >>> operate(inc,3) 4 >>> operate(dec,3) 2

Nadalje, funkcija može vratiti drugu funkciju.

 def is_called(): def is_returned(): print("Hello") return is_returned new = is_called() # Outputs "Hello" new()

Izlaz

 zdravo

Ovdje is_returned()je ugniježđena funkcija koja se definira i vraća svaki put kad nazovemo is_called().

Konačno, moramo znati o Zatvaranjima u Pythonu.

Povratak na Dekoratore

Funkcije i metode nazivaju se pozivima kako se mogu nazvati.

U stvari, svaki objekt koji provodi posebnu __call__()metodu naziva se pozivom. Dakle, u najosnovnijem smislu, dekorator je poziv koji vraća pozivni.

U osnovi, dekorator uzima funkciju, dodaje neku funkcionalnost i vraća je.

 def make_pretty(func): def inner(): print("I got decorated") func() return inner def ordinary(): print("I am ordinary")

Kad u ljusci pokrenete sljedeće kodove,

 >>> ordinary() I am ordinary >>> # let's decorate this ordinary function >>> pretty = make_pretty(ordinary) >>> pretty() I got decorated I am ordinary

U gornjem primjeru prikazan make_pretty()je dekorator. U koraku dodjele:

 pretty = make_pretty(ordinary)

Funkcija ordinary()je ukrašena i vraćena funkcija je dobila ime pretty.

Vidimo da je funkcija dekoratora dodala novu funkciju izvornoj funkciji. Ovo je slično pakiranju poklona. Dekorator djeluje kao omot. Priroda predmeta koji je ukrašen (stvarni poklon iznutra) ne mijenja se. Ali sada izgleda lijepo (otkad je uređena).

Općenito, ukrašavamo funkciju i dodjeljujemo je kao,

 ordinary = make_pretty(ordinary).

Ovo je uobičajena konstrukcija i iz tog razloga Python ima sintaksu da to pojednostavi.

@Simbol možemo upotrijebiti zajedno s nazivom funkcije dekoratera i postaviti ga iznad definicije funkcije koja se ukrašava. Na primjer,

 @make_pretty def ordinary(): print("I am ordinary")

je ekvivalentan

 def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)

Ovo je samo sintaktički šećer za primjenu dekoratora.

Dekoriranje funkcija parametrima

Gornji uređivač bio je jednostavan i radio je samo s funkcijama koje nisu imale nikakve parametre. Što ako imamo funkcije koje uzimaju parametre poput:

 def divide(a, b): return a/b

Ova funkcija ima dva parametra, a i b. Znamo da će stvoriti pogrešku ako u b dodamo kao 0.

 >>> divide(2,5) 0.4 >>> divide(2,0) Traceback (most recent call last):… ZeroDivisionError: division by zero

Sada napravimo uređivač koji će provjeriti postoji li slučaj koji će uzrokovati pogrešku.

 def smart_divide(func): def inner(a, b): print("I am going to divide", a, "and", b) if b == 0: print("Whoops! cannot divide") return return func(a, b) return inner @smart_divide def divide(a, b): print(a/b)

Ova nova implementacija vratit će se Noneako se pojavi uvjet pogreške.

 >>> divide(2,5) I am going to divide 2 and 5 0.4 >>> divide(2,0) I am going to divide 2 and 0 Whoops! cannot divide

Na taj način možemo ukrasiti funkcije koje uzimaju parametre.

Oštri promatrač primijetit će da su parametri ugniježđene inner()funkcije unutar dekoratera jednaki parametrima funkcija koje on ukrašava. Uzimajući to u obzir, sada možemo napraviti opće dekoratere koji rade s bilo kojim brojem parametara.

U Pythonu se ta magija radi kao function(*args, **kwargs). Na ovaj način, argsbit će skup pozicijskih argumenata i kwargsbit će rječnik argumenata ključnih riječi. Primjer takvog dekoratera bit će:

 def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner

Lančani dekoratori u Pythonu

U Pythonu se može povezati više dekoratera.

To će reći, funkcija se može više puta ukrašavati različitim (ili istim) ukrasiteljima. Dekoratere jednostavno postavljamo iznad željene funkcije.

 def star(func): def inner(*args, **kwargs): print("*" * 30) func(*args, **kwargs) print("*" * 30) return inner def percent(func): def inner(*args, **kwargs): print("%" * 30) func(*args, **kwargs) print("%" * 30) return inner @star @percent def printer(msg): print(msg) printer("Hello")

Izlaz

 ****************************** %%%%%%%%%%%%%%%%%%%% %%%%%%%%%% Zdravo %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ********* *********************

Gornja sintaksa,

 @star @percent def printer(msg): print(msg)

je ekvivalentan

 def printer(msg): print(msg) printer = star(percent(printer))

Redoslijed kojim uređujemo ukrasne ukrase je važan. Da smo preokrenuli redoslijed kao,

 @percent @star def printer(msg): print(msg)

Rezultat bi bio:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%% ********************** ********** Zdravo ****************************** %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%

Zanimljivi članci...