[python] Dekorátor

Róbert Čerňanský hslists na zoznam.sk
Neděle Únor 20 16:02:50 CET 2011


On Wed, 16 Feb 2011 06:23:07 -0800 (PST)
Radek <radek.hnilica na gmail.com> wrote:

> Byl by tady někdo, kdo by mě mohl odkázat na rozumný studijní
> materiál, podtrhuji rozumný, ze kterého bych byl schopen pochopit, jak
> funguje dekorátor který definuju jako třídu.  S dekorátory jako
> funkcemi jsem se nějak popasoval, tedy aspoň si to myslim, ale u té
> třídy je to pro mě černá magie.

S tými rozumnými materiálmi je to bieda.  Hlavne sa mi zdá, že
oficiálny Python Tutorial akosi zaostáva.  Pri niekoľkonásobnom
prečítaní relevantných (a roztrúsených) častí oficiálnej dokumentácie
[1], [2], [3] a PEP 318 [4] sa to akosi dá pochopiť.  Pokúsim sa to
vysvetliť nižsie.

> Pokud nemá dekorátor parametry, tak se zdá že to nějak chápu.  Do
> __init__ je předána dekorovaná fuknce a do __call__ jsou pak předávány
> argumenty té funkce viz:
> 
> class mydec:
>     def __init__(self, fce):
>         self.fce = fce   # Store, we will use it in __call__
> 
>     def __call__(self, *args, **kwds):
>         ....pre
>         result = self.fce(*args, **kwds)
>         .....post
>         return result
> 
> @mydec
> def myfunc(*args, **kwds):
>     pass
> 
> Ale pokud má mít dekorátor parametry, tak jsem sice našel/zkonstruoval
> kód který se zdá fungovat, ale plně mu nerozumím.
[...]
> Jen tím že chci aby měl dekorátor parametry, se tak radikálně změní
> kód dekorátoru.
> Potřeboval bych nějak po lopatě vědět, co se uvnitř děje, protože
> cítím že ty dekorátory budu určitě používat ještě mnohem divočeji.

Vo všeobecnosti je dekorátor funkcia, ktorá vracia funkciu.  V Pythone
je dekorátor „niečo, čo sa dá zavolať“ a po zavolaní to vráti tiež
„niečo, čo sa dá zavolať“.  To „niečo, čo sa dá zavolať“ je akýkoľvek
objekt, ktorý má metódu __call__() – čiže funkcia; trieda; inštancia
tredy, ktorá definuje __call__(); jedným slovom callable.  To prvé
callable reprezentuje samotný dekorátor a to druhé callable
reprezentuje, *v prípade dekorátora bez parametrov*, odekorovanú
funkciu (čize funkciu, ktorá už je „oblečená“ do dekorátora).

Dekorátor (to, čo je za @) je volaný pri *definícii* funkcie, teda nie
pri jej zavolaní.  Pri tomto volaní sa vráti uz spomínané druhé
callable, ktoré reprezentuje odekorovanú funkciu (v prípade dekorátora
bez parametrov).  To je potom volané zakaždým, keď je volaná
dekorovaná funkcia (čiže tá, ktorá má nad sebou @).

_Dekorátor bez parametrov_, je zavolaný s jedným parametrom a tým je
dekorovaná funkcia.

Čiže keď zoberieme Tvoj príklad dekorátora bez parametrov, tak mydec
je zavolaný pri definícii myfunc() a dostane myfunc ako parameter.
Keď zoberieme do úvahy, že mydec je trieda, tak jej zavolanie vlastne
znamená vytvorenie inštancie.  Táto vzniknutá inštancia má __call__()
a teda spĺňa podmnienku, že je callable.  Vniknutá inštancia teda
reprezentuje odekorovanú myfunc() a jej metóda __call__() je zavolaná
pri každom volaní myfunc().

_Dekorátor s parametrami_, je zavolaný iba s tými parametrami, s
ktorými bol nadefinovaný.  Čiže *nedostane dekorovanú funkciu*.
Callable, ktoré takýto dekorátor vráti reprezentuje *ďalší dekorátor*.
Tento ďalší dekorátor, je už „normálny“ dekorátor bez parametrov
popísaný vyššie.  Je zavolaný tiež pri definícii funkcie (hneď za
dekorátorom s parametrami, ktorý ho vrátil) a dostane dekorovanú
funkciu ako parameter.

Dúfam, že som Ťa ešte viac nepoplietol. ;-)

[1] - http://docs.python.org/py3k/glossary.html#term-decorator
[2] -
http://docs.python.org/py3k/reference/compound_stmts.html#function
[3] -
http://docs.python.org/py3k/whatsnew/2.4.html?highlight=decorator

Robo


-- 
Robert Cernansky
E-mail: hslists na zoznam.sk
Jabber: hs na jabber.sk


Další informace o konferenci Python