2.23. Dekorátory

Dekorátor je kus syntaxe, který obaluje funkci (nebo metodu) do jiné funkce. Obal může přidat chování před a po původním volání, nahradit návratovou hodnotu nebo připojit metadata. Tvar je:

@wrapper
def f():
    ...

Řádek @wrapper je ekvivalentní f = wrapper(f) – funkce f se sestaví normálně, pak se předá wrapper a výsledek se znovu naváže na jméno f.

A function entering a "wrapper" box on the left and emerging on the right as a decorated function.

Dekorátor přijme funkci a vrátí novou funkci.

2.23.1. Vestavěné dekorátory metod

Několik dekorátorů je součástí Pythonu a používá se uvnitř těl tříd.

2.23.1.1. @property

Promění metodu v vypočítaný atribut. Volající k němu přistupuje, jako by to byl obyčejný atribut (bez závorek), ale při každém čtení se spustí metoda:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14159 * self.radius * self.radius

c = Circle(5)
print(c.area)             # 78.53975  -- no parentheses

Použijte jej, když atribut, který vypadá jednoduše, za sebou potřebuje drobný výpočet. Pokud je práce náročná, dejte přednost běžné metodě – volající neočekávají, že čtení atributu bude pomalé.

2.23.1.2. @classmethod

Definuje metodu, která jako svůj první argument dostává třídu místo instance. První parametr se konvenčně pojmenovává cls:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def origin(cls):
        return cls(0, 0)

p = Point.origin()

Metody třídy jsou standardní způsob, jak poskytnout alternativní konstruktory – tovární funkce, které vracejí instanci sestavenou nestandardním způsobem.

2.23.1.3. @staticmethod

Definuje metodu, která nedostává ani instanci, ani třídu – je to prostě obyčejná funkce, která žije v jmenném prostoru třídy z organizačních důvodů:

class Temperature:
    @staticmethod
    def c_to_f(c):
        return c * 9 / 5 + 32

Temperature.c_to_f(100)   # 212.0

Používejte ji střídmě; pokud funkce opravdu nemá nic společného se stavem třídy, je obvykle čistší obyčejná funkce na úrovni modulu.

2.23.2. Napsání vlastního dekorátoru

Dekorátor je funkce, která přijme funkci a vrátí funkci. Minimální tvar:

def log_calls(func):
    def wrapper(*args, **kwargs):
        print("calling", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a, b):
    return a + b

add(2, 3)

Výstup:

calling add

wrapper uzavírá nad func a vše do něj předává. *args / **kwargs mu umožňuje fungovat na jakékoli funkci, nejen na dvouargumentových. Tento vzor je základem propracovanějších dekorátorů (měření času, kešování, opakování při selhání), ale jádro je vždy stejné: přijmout funkci, vrátit funkci.