Python Decorators: Kako ga uporabljati in zakaj?

Dekorater sprejme funkcijo, doda nekaj funkcionalnosti in jo vrne. V tej vadnici boste izvedeli, kako lahko ustvarite dekorater in zakaj bi ga morali uporabljati.

Dekoraterji v Pythonu

Python ima zanimivo funkcijo, imenovano dekoraterji, ki obstoječi kodi doda funkcionalnost.

To se imenuje tudi metaprogramiranje, ker del programa poskuša spremeniti drug del programa v času prevajanja.

Predpogoji za učenje dekoraterjev

Da bi razumeli dekoraterje, moramo najprej vedeti nekaj osnovnih stvari v Pythonu.

Udobno nam mora biti dejstvo, da so vse stvari v Pythonu (da! Tudi razredi) predmeti. Imena, ki jih definiramo, so preprosto identifikatorji, vezani na te predmete. Funkcije niso nobena izjema, so tudi predmeti (z atributi). Na isti funkcijski objekt je mogoče vezati različna imena.

Tu je primer.

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

Izhod

 Živjo živjo

Ko zaženete kodo, obe funkciji firstin seconddaje enake moči. Tu se imena firstin secondsklici na isti objekt funkcije.

Zdaj začnejo stvari postajati bolj čudne.

Funkcije lahko prenesete kot argumente drugi funkciji.

Če imate uporabljene funkcije, kot so map, filterin reducev Python, potem že veste o tem.

Takšne funkcije, ki druge funkcije jemljejo kot argumente, imenujemo tudi funkcije višjega reda . Tu je primer takšne funkcije.

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

Funkcijo prikličemo na naslednji način.

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

Poleg tega lahko funkcija vrne drugo funkcijo.

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

Izhod

 zdravo

Tu is_returned()je ugnezdena funkcija, ki je definirana in vrnjena vsakič, ko pokličemo is_called().

Na koncu moramo vedeti še o zapiranjih v Pythonu.

Vrnitev k Dekoraterjem

Funkcije in metode se imenujejo klicajoče, kot jih lahko imenujemo.

Pravzaprav se vsak predmet, ki izvaja posebno __call__()metodo, imenuje klicajni. Torej, v najosnovnejšem smislu je dekorater klicatelj, ki vrne klicatelja.

Dekorater v bistvu sprejme funkcijo, doda nekaj funkcionalnosti in jo vrne.

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

Ko zaženete naslednje kode v lupini,

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

V zgornjem make_pretty()primeru je dekorater. V koraku dodelitve:

 pretty = make_pretty(ordinary)

Funkcija ordinary()je bila okrašena in vrnjena funkcija je dobila ime pretty.

Vidimo, da je funkcija dekoraterja dodala prvotno funkcijo nekaj novih funkcij. To je podobno kot pakiranje darila. Dekorater deluje kot ovoj. Narava predmeta, ki je okrašen (dejansko darilo znotraj), se ne spremeni. Zdaj pa je videti lepo (saj je okrašena).

Na splošno funkcijo okrasimo in ji dodelimo kot,

 ordinary = make_pretty(ordinary).

To je pogosta konstrukcija in zato ima Python sintakso, ki to poenostavlja.

@Simbol lahko uporabimo skupaj z imenom funkcije dekoraterja in ga postavimo nad definicijo funkcije, ki jo želimo okrasiti. Na primer

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

je enakovredno

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

To je samo skladenjski sladkor za izvedbo dekoraterjev.

Okrasitev funkcij s parametri

Zgornji dekorater je bil preprost in je deloval samo s funkcijami, ki niso imele nobenih parametrov. Kaj pa, če bi imeli funkcije, ki so zajemale parametre, kot so:

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

Ta funkcija ima dva parametra, a in b. Vemo, da bo prišlo do napake, če bomo b podali kot 0.

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

Zdaj pa naredimo dekorater, ki bo preveril ta primer, ki bo povzročil napako.

 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)

Ta nova izvedba se bo vrnila, Noneče se pojavi pogoj napake.

 >>> 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 ta način lahko okrasimo funkcije, ki upoštevajo parametre.

Navdušen opazovalec bo opazil, da so parametri vgnezdene inner()funkcije znotraj okrasja enaki parametrom funkcij, ki jih okrasi. Ob upoštevanju tega lahko zdaj izdelamo splošne dekoraterje, ki delujejo s poljubnim številom parametrov.

V Pythonu je ta magija narejena kot function(*args, **kwargs). Na ta način argsbo nabor položajnih argumentov in kwargsbo slovar argumentov ključnih besed. Primer takega dekoraterja bo:

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

Veriženje dekoraterjev v Pythonu

V Pythonu je mogoče privezati več okrasiteljev.

To pomeni, da lahko funkcijo večkrat okrasimo z različnimi (ali enakimi) dekoraterji. Dekoraterje preprosto postavimo nad želeno funkcijo.

 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")

Izhod

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

Zgornja sintaksa,

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

je enakovredno

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

Vrstni red, v katerem povezujemo dekoraterje, je pomemben. Če bi obrnili vrstni red,

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

Rezultat bi bil:

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

Zanimive Članki...