Dekorátory
Co jsou dekorátory
Tato kapitola je o dalším pythonovském syntaktickém cukru, dekorátorech. Dekorátory nejsou zrovna tím hlavním a nejdůležitejším rysem Pythonu, ale můžete se s nimi setkat na mnoha místech v dokumentaci, tak je asi dobré vědět o co jde.
Dekorátorem se nazývá nějaká funkce nebo třída, která rozšiřuje nebo mění funkčnost jiné třídy nebo funkce. Používají se tam, kde nechcete funkčnost původní funkce/třídy měnit, ale chcete, aby se funkce/třída daného jména chovala trochu jinak.
Jak víte, funkce i třídy jsou v Pythonu objekty, takže je můžete přiřazovat do proměnných, vytvářet uvnitř jiných funkcí atp.
Uvažujte následující příklad. Řekněme, že máte program, který vypisuje sposutu textu
pomocí funkce print
. Vy ale chcete mít možnost všechen výpis nějak vypnout.
Místo funkce print
budete chctít použít tuto funkci:
try:
if(debug == True):
print(*args, **kwargs)
except NameError: # debug neni definovan
pass
print(*args, **kwargs)
,
nicméně dekorátory jako takové můžete psát v jakékoliv verzi Pythonu. Speciální syntax pro dekorátory
(popisovaná níže) funguje od Pythonu 2.6.
Funkce vypíše text jen když je definována globální proměnná debug
a má hodnotu true
.
>>> debug = True
>>> xprint("Hello World")
Hello World
Bezva. Teď můžete všude v kódu nahradit print
za xprint
.
To je ale zbytečně moc práce. Dekorace je o tom, že přímo změníte funkčnost
print
. Co takhle využít toho, že funkce je jen objekt,
jehož jménu můžeme přiřadit cokoliv jiného? Třeba takto:
>>> print("Hello World")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in xprint
File "<stdin>", line 4, in xprint
File "<stdin>", line 4, in xprint
...
RuntimeError: maximum recursion depth exceeded in comparison
Tak to nevyšlo. Funkce xprint
volá print
, což se stalo
jiné jméno pro xprint
, takže dochází k rekurzivnímu volání xprint
a kód funkce print
je ztracen.
Chtělo by si to print
nejdřív uložit do jiného jména a to použít uvnitř xprint
.
Zkusím to udělat tak, abych nemusel nové jméno definovat v globálním jmenném prostoru.
Zabalím proto funkci xprint
i s novým jménem do jiné funkce. Tato funkce
funkci xprint
vrátí jako výsledek svého volání.
fce = print
def xprint(*args, **kwargs):
try:
if(debug == True):
fce(*args, **kwargs)
except NameError: # debug neni definovan
pass
return xprint
Hello World
>>> print = dekorator_print()
>>> print("Hello World")
>>> debug = True
>>> print("Hello World")
Hello World
To je mnohem lepší. Co to napsat ale trochu víc obecně,
abychom mohli takto „odekorovat“ více funkcí?
Můžeme chtít zapínat/vypínat při debugování nejen print
…
Stačí vcelku banální úprava (dekorátor jsem přejmenoval, protože už není míněn jen pro
print
):
def xprint(*args, **kwargs):
try:
if(debug == True):
fce(*args, **kwargs)
except NameError: # debug neni definovan
pass
return xprint
>>> print("Hello World")
>>> debug = True
>>> print("Hello World")
Hello World
Syntaktický cukr
Teď už víte čemu se říká dekorátor. Pokud budte chtít nějaký použít na vlastní funkci (nebo metodu, třídu), můžete to udělat podobně jako v příkladu výše, nebo pomocí syntaxe se zavynáčem, kterou vám k tomu Python poskytuje.
Následující dva kódy jsou ekvivalentní:
print("info: "+text)
debuginfo = dekorator_debug(debuginfo)
def debugwarning(text):
print("debugwarning: "+text)
debugwarning = dekorator_debug(debugwarning)
def debuginfo(text):
print("info: "+text)
@dekorator_debug
def debugwarning(text):
print("debugwarning: "+text)
Dekorátory Pythonu
Python definuje celou řadu dekorátorů. Většinou na ně narazíte při pročítání dokumentace.
Třeba takový dekorátor @classmethod se používá pro vytváření třídních metod tříd (metody, které jako první argument nedostanou instanci třídy, ale třídu).
def instance_method(self):
print(self)
@classmethod
def class_method(cls):
print(cls)
>>> a.instance_method()
<__main__.Test object at 0x7fd5e2088fd0>
>>> a.class_method()
<class '__main__.Test'>
Pokud vás tato kapitola zaujala, doporučuji přečíst A guide to Python's function decorators.