Python Decorators: How to use and why?

Dekoratér prevezme funkciu, pridá určitú funkcionalitu a vráti ju. V tomto návode sa dozviete, ako môžete vytvoriť dekoratér a prečo by ste ho mali používať.

Dekoratéri v Pythone

Python má zaujímavú funkciu zvanú dekoratéri, ktorá pridáva funkčnosť existujúcemu kódu.

Toto sa tiež nazýva metaprogramovanie, pretože časť programu sa pokúša upraviť inú časť programu v čase kompilácie.

Predpoklady na učenie dekoratérov

Aby sme porozumeli dekoratérom, musíme v Pythone najskôr vedieť niekoľko základných vecí.

Musíme byť spokojní s tým, že všetko v Pythone (Áno! Dokonca aj triedy), sú objekty. Názvy, ktoré definujeme, sú jednoducho identifikátory viazané na tieto objekty. Funkcie nie sú výnimkou, sú to tiež objekty (s atribútmi). Na ten istý funkčný objekt je možné naviazať rôzne rôzne názvy.

Tu je príklad.

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

Výkon

 Ahoj ahoj

Pri spustení kódu, obe funkcie firsta seconddávajú rovnaký výstup. Tu názvy firsta secondodkazujú na rovnaký funkčný objekt.

Teraz to začne byť čudnejšie.

Funkcie je možné odovzdať ako argumenty inej funkcii.

Ak ste použili funkcie, ako je map, filtera reducev Pythone, potom už viete o tom.

Také funkcie, ktoré ako argumenty berú iné funkcie, sa nazývajú aj funkcie vyššieho rádu . Tu je príklad takejto funkcie.

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

Funkciu vyvoláme nasledovne.

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

Ďalej môže funkcia vrátiť inú funkciu.

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

Výkon

 Ahoj

Tu is_returned()je vnorená funkcia, ktorá je definovaná a vrátená zakaždým, keď zavoláme is_called().

Nakoniec musíme vedieť o uzáveroch v Pythone.

Vraciame sa k dekoratérom

Funkcie a metódy sú volány callable pretože možno nazvať.

V skutočnosti je každý objekt, ktorý implementuje špeciálnu __call__()metódu, nazývaný volaný. Takže v najzákladnejšom zmysle je dekoratér volaný, ktorý vracia volaný.

V zásade dekoratér prevezme funkciu, pridá určitú funkcionalitu a vráti ju.

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

Keď spustíte nasledujúce kódy v prostredí shell,

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

V príklade uvedenom vyššie make_pretty()je dekoratér. V kroku priradenia:

 pretty = make_pretty(ordinary)

Funkcia bola ordinary()ozdobená a vrátená funkcia dostala meno pretty.

Vidíme, že funkcia dekorátora pridala k pôvodnej funkcii nejaké nové funkcie. Je to podobné ako s balením darčeka. Dekorátor funguje ako obal. Povaha zdobeného predmetu (skutočný darček vo vnútri) sa nemení. Ale teraz to vyzerá pekne (keďže to bolo vyzdobené).

Spravidla zdobíme funkciu a znova ju priradíme ako,

 ordinary = make_pretty(ordinary).

Toto je bežný konštrukt a z tohto dôvodu má Python syntax, ktorá to zjednodušuje.

Môžeme použiť @symbol spolu s názvom funkcie dekorátora a umiestniť ho nad definíciu funkcie, ktorá sa má zdobiť. Napríklad,

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

je ekvivalentné k

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

Toto je iba syntaktický cukor na implementáciu dekoratérov.

Zdobenie funkcií pomocou parametrov

Vyššie uvedený dekoratér bol jednoduchý a pracoval iba s funkciami, ktoré nemali žiadne parametre. Čo keby sme mali funkcie, ktoré by brali parametre ako:

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

Táto funkcia má dva parametre, a a b. Vieme, že ak zadáme b ako 0, bude to mať chybu.

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

Teraz urobme dekoratér, ktorý skontroluje tento prípad, ktorý spôsobí chybu.

 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)

Táto nová implementácia sa vráti, Noneak dôjde k chybovému stavu.

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

Týmto spôsobom môžeme zdobiť funkcie, ktoré berú parametre.

Vášnivý pozorovateľ si všimne, že parametre vnorenej inner()funkcie vo vnútri dekorátora sú rovnaké ako parametre funkcií, ktoré zdobí. Ak to vezmeme do úvahy, teraz môžeme vyrobiť všeobecné dekoratéry, ktoré pracujú s ľubovoľným počtom parametrov.

V Pythone sa táto mágia robí ako function(*args, **kwargs). Týmto spôsobom argsbude n-ticou pozičných argumentov a kwargsbude slovníkom argumentov kľúčových slov. Príkladom takéhoto dekoratéra bude:

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

Reťazenie dekoratérov v Pythone

V Pythone je možné reťaziť viac dekoratérov.

To znamená, že funkcia môže byť zdobená viackrát rôznymi (alebo rovnakými) dekorátormi. Dekorátory jednoducho umiestnime nad požadovanú funkciu.

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

Výkon

 ******************************** %%%%%%%%%%%%%%%%%%%%% %%%%%%%%%% Ahoj %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ********* **********************

Vyššie uvedená syntax,

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

je ekvivalentné k

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

Na poradí, v akom spájame dekoratérov, záleží. Keby sme obrátili poradie ako,

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

Výstup by bol:

 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ********************* ********** Ahoj ****************************** %%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%

Zaujímavé články...