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.