Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

16. diel - Dekorátory v Pythone

V predchádzajúcom kvíze, Kvíz - Dátum a čas v Pythone, sme si overili nadobudnuté skúsenosti z predchádzajúcich lekcií.

V nasledujúcom tutoriále objektovo orientovaného programovania v Pythone sa bližšie zoznámime s dekorátormi. Vysvetlíme si, že dekorátor je nástroj, ktorý umožňuje pridať novú funkčnosť k existujúcim objektom bez modifikácie ich štruktúry. Je to forma metaprogramovania, kde jeden kód dokáže ovplyvňovať iný kód.

Dekorátory v Pythone

Dekorátory v Pythone ponúkajú množstvo výhod, ktoré zjednodušujú a optimalizujú kód. Umožňujú nám centralizovať opakujúce sa segmenty kódu, namiesto ich mnohonásobného písania v rôznych funkciách. Okrem toho vďaka dekorátorom dokážeme pridávať špecifické funkcie, ako je meranie času behu, logovania alebo overovania prístupových práv. Navyše vďaka nim dokážeme lepšie oddeliť hlavnú logiku programu od doplnkových funkcií, čo vedie k čistejšej a ľahko čitateľnej štruktúre kódu.

Dekorátory sú náročná téma. Je preto veľmi dôležité starostlivo analyzovať všetky ukážky kódu v lekcii, skúsiť si ich vo vlastnom IDE modifikovať a neprechádzať ďalej v tutoriáli, pokiaľ kód skutočne plne nepochopíte.

Základná syntax dekorátorov

Pozrime sa na príklad jednoduchého dekorátora. Základná idea dekorátora spočíva v tom, že máme funkciu (dekorátor), ktorá prijíma inú funkciu ako argument a vracia novú funkciu, ktorá rozširuje alebo mení správanie pôvodnej funkcie:

V tomto príklade:

  • muj_dekorator() je náš dekorátor. Prijíma ako argument referenciu na funkciu, ktorú chceme dekorovať. Túto referenciu označujeme ako func (pozri nižšie).
  • obalena_funkce() je vnútorná funkcia, ktorá "obaľuje" dekorovanú funkciu a pridáva k nej nové správanie. Keď je táto funkcia volaná, vypíše správu pred spustením dekorovanej funkcie func() a ďalšiu správu po jej dokončení,
  • použitím syntaxe @muj_dekorator dekorujeme funkciu ukazkova_funkce(). V podstate to znamená, že ukazkova_funkce() je odovzdaná ako argument (teda referencie) do muj_dekorator(), a tak sa stáva tou func a func() v dekorátore.
Funkcia ako objekty prvej triedy

Malo by nás zaraziť použitie funkcií func a obalena_funkce v kóde bez zátvoriek. Ide o kľúčový koncept v Pythone a týka sa spôsobu, ako Python zaobchádza s funkciami ako objekty prvej triedy (first-class objects). Keď odkazujeme na funkciu bez zátvoriek, odkazujeme sa na funkciu samotnú, nie na výsledok jej volania. Inými slovami, funkcia je v Pythone objektom ako každý iný (reťazec, číslo, zoznam a podobne). Preto je možné na ňu odkazovať, odovzdávať ju ako argument inej funkcii alebo ju vrátiť ako návratovú hodnotu z inej funkcie.

V našom kóde dekorátor pomenovaný muj_dekorator() prijíma ako argument odkaz na funkciu ukazkova_funkce(). To sa stane vďaka tomu, že funkciu ukazkova_funkce() označíme pomocou syntaxe @muj_dekorator. Vnútri funkcie obalena_funkce() potom voláme pôvodnú funkciu ukazkova_funkce() pomocou func(). Dekorátor vracia referenciu na funkciu obalena_funkce a nie jej výsledok, pretože nevoláme obalena_funkce() s koncovými zátvorkami. Keď potom voláme v programe funkciu ukazkova_funkce(), v skutočnosti voláme funkciu obalena_funkce(), ktorej kód sa vykoná.

Ukážme si to na jednoduchom príklade:

Výstup v konzole:

Dekorovanie funkcií s parametrami

Pokiaľ chceme dekorovať funkcie, ktoré prijímajú parametre, musíme v dekorátore s týmito parametrami správne zaobchádzať. K tomu nám slúžia *args a **kwargs, čo sú nám už známe konvencie na zachytenie ľubovoľného počtu pozičných a kľúčových argumentov. Predstavme si, že chceme vytvoriť dekorátor, ktorý loguje argumenty funkcie:

Vďaka *args a **kwargs je náš dekorátor flexibilný a dokáže dekorovať akúkoľvek funkciu bez ohľadu na to, koľko a akých argumentov má.

Návratovými hodnotami *args a **kwargs sú tuple a slovník. Tieto dátové typy ešte nepoznáme. Zoznámime sa s nimi až v kurze Kolekcia v Pythone. Obaja si ale kedykoľvek ľahko prevedieme na zoznam. Pre *args použijeme seznam_args = list(args) a pre **kwargs získame zoznam dvojíc (názov argumentu, hodnota) pomocou seznam_kwargs = list(kwargs.items()).

Viacnásobné dekorovanie

Viacnásobné dekorovanie spočíva v aplikácii viac ako jedného dekorátora na jednu funkciu. Dekorátory sa aplikujú vo vrstvách, pričom prvý dekorátor, ktorý je aplikovaný, je ten najbližšie k funkcii. Posledný dekorátor je ten, ktorý sa nachádza najvyššie.

Pokiaľ si predstavíme dekorátory ako vrstvy obalu okolo funkcie, koncept viacnásobného dekorovania sa stáva jasnejším. Každý dekorátor pridáva ďalšiu vrstvu obalu.

Rozšírme si príklad s logovaním:

Prvým dekorátorom, ktorý sa aplikuje, je @zmer_cas, ktorý obaľuje funkciu vypocitej(). Potom sa aplikuje dekorátor @zaloguj_volani, ktorý obaľuje celú kombináciu zmer_cas(vypocitej).

Akonáhle zavoláme funkciu vypocitej(1, 2, c=3), v skutočnosti voláme:

Keď odstránime dekorátory nad vypocitej() a vložíme vyššie uvedenú konštrukciu na miesto priameho volania vypocitej(1, 2, c=3), dostaneme rovnaký výstup, ako pri použití dekorátorov. Zápis zaloguj_volani(zmer_cas(vypocitej))(1, 2, c=3) je veľmi vhodné dôkladne preštudovať - poskytuje presný obraz toho, ako dekorátory pracujú.

V kóde sa krok za krokom stane toto:

  • funkcia zaloguj_volani() sa spustí a vypíše: Funkce vypocitej() byla zavolána s pozičními argumenty: (1, 2) a klíčovými argumenty: {'c': 3}.,
  • funkcia zaloguj_volani() potom volá funkciu zmer_cas(), ktorá meria dobu behu funkcie vypocitej() a spustí ju,
  • funkcia vypocitej() sa vykoná a vráti Výsledek výpočtu je: 6,
  • funkcia zmer_cas() potom dokončí meranie času a vypíše: Čas běhu funkce vypocitej(): 1.00111 sekund.
Je teda dôležité si uvedomiť, že dekorátory sa najskôr aplikujú vo vzostupnom poradí, ale spúšťajú sa v zostupnom poradí.

V budúcej lekcii, Dekorátory druhýkrát - Parametrické a triedne dekorátory , budeme v téme dekorátorov pokračovať.


 

Predchádzajúci článok
Kvíz - Dátum a čas v Pythone
Všetky články v sekcii
Objektovo orientované programovanie v Pythone
Preskočiť článok
(neodporúčame)
Dekorátory druhýkrát - Parametrické a triedne dekorátory
Článok pre vás napísal Karel Zaoral
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Karel Zaoral
Aktivity