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

17. diel - Dekorátory druhýkrát - Parametrické a triedne dekorátory

V predchádzajúcej lekcii, Dekorátory v Pythone , sme sa zoznámili s dekorátormi a vysvetlili si princíp ich použitia.

V dnešnom tutoriáli objektovo orientovaného programovania v Pythone budeme pokračovať v práci s dekorátormi. Naučíme sa ich parametrizovať a aplikovať na triedu. V závere lekcie si tému zhrnieme a ukážeme si celý postup vytvorenia dekorátora v nadväzujúcich krokoch.

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.

Dekorátory s parametrami

Zatiaľ čo doteraz naše dekorátory prijímali ako argumenty iba funkcie, Python vie vytvoriť aj dekorátory, ktoré samy o sebe prijímajú parametre. Vďaka tomu dokážeme jednoducho vytvárať dekorátory, ktoré sa chovajú rôzne na základe poskytnutých parametrov.

Využitie dekorátorov s parametrami je veľmi často vidieť napríklad v rôznych webových frameworkoch, kde je možné konfigurovať, ako sa majú funkcie (napr. spracovanie HTTP požiadaviek) správať na základe rôznych argumentov.

Keď chceme vytvoriť dekorátor s parametrami, potrebujeme tri úrovne funkcií:

  • vonkajšiu funkciu, ktorá prijíma parametre dekorátora,
  • vnútornú funkciu (dekorátor), ktorá prijíma funkciu, ktorú chceme dekorovať,
  • obalenú funkciu - to je tá skutočná funkcia, ktorá rozširuje správanie pôvodnej dekorovanej funkcie a je zavolaná namiesto nej.
Použitím dekorátora s parametrami vytvárame v podstate "továreň na dekorátory":

Toto je práve tá "továreň na dekorátory" - možnosť vytvoriť dekorátor na mieru podľa našich potrieb.

Pozrime sa bližšie na kód. Náš "vonkajší" dekorátor zprava_dekorator() prijíma argument zprava a vracia skutočný dekorátor vnitrni_dekorator(). Ten následne obaľuje naše funkcie secti() a nasob(). Vďaka tomu môžeme ľahko meniť obsah správy pre rôzne funkcie bez toho, aby sme museli meniť samotný dekorátor. Vnútorná funkcia obalena_funkce() pozná hodnotu premennej zprava iba z takzvaného vonkajšieho kontextu, čo je ukážkou mechanizmu zvaného closure.

Uzáver (closure)

Uzáver (closure) je funkcia, ktorá si "pamätá" svoje voľné premenné z okolitého kontextu, v ktorom bola definovaná, a dokáže k nim pristupovať aj po skončení tohto kontextu. Jednoducho povedané, closure je funkcia spoločne s nejakým zachyteným kontextom. V kóde je tento "kontext" tvorený premennými, ktoré sú dostupné v okamihu vytvorenia closure:

V tomto príklade je vnitrni_funkce() uzáverom, ktorý má prístup k premennej delenec aj po tom, čo vnejsi_funkce() skončila.

Vysvetlenie, prečo si funkcia "pamätá" svoj kontext, je spojené s tým, ako Python funguje "pod kapotou". Uzávery v Pythone sú realizované prostredníctvom objektu, ktorý reprezentuje funkciu. Tento objekt obsahuje niekoľko atribútov, ktoré uchovávajú informácie o funkcii a jej kontexte. Jedným z týchto atribútov je __closure__, ktorý obsahuje referencie na voľné premenné z kontextu, kde bola funkcia vytvorená. Keď definujeme vnorenú funkciu vo vnútri inej funkcie a táto vnorená funkcia odkazuje na premenné z vonkajšej funkcie, Python closure vytvorí automaticky.

Triedne dekorátory

Rovnako ako sme vytvárali dekorátory pre funkcie, budeme taktiež tvoriť dekorátory pre triedy. Triedne dekorátory obvykle pridávajú, upravujú alebo rozširujú funkcionalitu triedy.

Rovnako ako pri funkčných dekorátoroch, tak aj triedny dekorátor je funkciou, ktorá prijíma triedu ako argument a vracia upravenú alebo novú triedu. Pozrime sa na príklad:

Výhodami triednych dekorátorov sú:

  • modularita - oddelíme rôzne funkcionality do rôznych dekorátorov a aplikujeme ich podľa potreby,
  • opakovaná použiteľnosť - raz vytvorený dekorátor je možné použiť na viacerých triedach,
  • rozšíriteľnosť - ľahko rozšírime funkcie existujúcich tried bez úpravy pôvodného kódu.
Pozor si musíme dať na:
  • komplexitu - rovnako ako s funkčnými dekorátormi je dôležité nepreplácať dekorátory príliš mnohými funkciami. Výsledkom budú zmätenie a komplikácie pri čítaní kódu,
  • dedičnosť - dekorátor samozrejme interaguje s dedičnosťou. Pokiaľ trieda dedí z inej triedy, dekorátor môže výrazne ovplyvniť správanie potomka.
Vytváranie triednych dekorátorov je už naozaj veľmi pokročilá technika (aj samotné funkčné dekorátory nie sú úplne triviálne), ale je na nezaplatenie v určitých situáciách, kedy potrebujeme meniť správanie tried dynamicky a modulárne.

Vstavané dekorátory

Python ponúka niekoľko vstavaných dekorátorov, ktoré umožňujú rýchlo a efektívne rozšíriť funkčnosť vašich tried a funkcií. V kurze sme sa už zoznámili s dekorátormi @staticmethod a @classmethod. S dekorátorom @property sa zoznámime v lekcii Vlastnosti v Pythone. Vstavané dekorátory v Pythone uľahčujú rad bežných programátorských úloh a umožňujú efektívnu a elegantnú implementáciu funkcionalít. Je naozaj dôležité sa s nimi dobre zoznámiť, pretože ich budeme často stretávať v praxi.

Vytváranie vlastných dekorátorov

Už sme si ukázali, ako dekorátory fungujú, a videli sme, ako dokážu meniť správanie funkcií bez toho, aby sme ich (tie funkcie) museli priamo upravovať. Pretože ide o pomerne náročnú tému, celú lekciu si teraz zhrnieme a pozrieme sa, ako vytvoriť vlastný dekorátor od základu.

Návrh dekorátora

Základným krokom pri vytváraní dekorátora je napísať funkciu (tj dekorátor), ktorý prijíma funkciu ako argument a vracia inú funkciu:

Parametrizácia dekorátora

Ako sme si ukázali, dekorátor vie prijímať parametre. Na to potrebujeme ďalšiu vonkajšiu funkciu, ktorá obklopí náš dekorátor:

Použitie dekorátora

Na aplikáciu dekorátora na funkciu použijeme @ syntax:

Volanie pozdrav() je možné bez použitia @dekorator_s_parametry("pozdrav") nahradiť zápisom dekorator_s_parametry("pozdrav")(pozdrav)(). Keď každý náš vytvorený dekorátor dokážeme zapísať aj týmto spôsobom, je to dobrá známka toho, že problematike dobre rozumieme.

Dekorátory sú silným nástrojom, pokiaľ sú používané správne. Umožňujú nám dodať dodatočné správanie funkciám alebo triedam v modulárnej a čitateľnej forme. Je ale veľmi dôležité dbať na to, aby kód zostal čitateľný a nesnažiť sa natlačiť za každú cenu príliš veľa funkčnosti do jedného dekorátora.

V nasledujúcej lekcii, Vlastnosti v Pythone druhýkrát - Pokročilé vlastnosti a dedenie , sa v práci s vlastnosťami zameriame na dedenie, časté chyby a vytváranie vlastných dekorátorov pre vlastnosti.


 

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