2.23. Dekoratori

Dekorator je dio sintakse koji omata funkciju (ili metodu) u drugu funkciju. Omotač može dodati ponašanje prije i nakon izvornog poziva, zamijeniti povratnu vrijednost ili pridodati metapodatke. Oblik je:

@wrapper
def f():
    ...

Redak @wrapper ekvivalentan je s f = wrapper(f) – funkcija f se gradi normalno, zatim predaje funkciji wrapper, a rezultat se ponovno veže na ime f.

Funkcija ulazi u kutiju "omotača" s lijeve strane i izlazi s desne kao dekorirana funkcija.

Dekorator prima funkciju i vraća novu funkciju.

2.23.1. Ugrađeni dekoratori metoda

Nekoliko dekoratora dolazi s Pythonom i koristi se unutar tijela klasa.

2.23.1.1. @property

Pretvara metodu u izračunati atribut. Pozivatelj mu pristupa kao da je običan atribut (bez zagrada), no pri svakom čitanju izvršava se 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

Koristi ga kad atribut koji izgleda jednostavno iza sebe treba sitan izračun. Ako je posao skup, radije koristi običnu metodu – pozivatelji ne očekuju da je čitanje atributa sporo.

2.23.1.2. @classmethod

Definira metodu koja kao prvi argument prima klasu umjesto instance. Prvi parametar se konvencionalno imenuje 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()

Metode klase standardni su način pružanja alternativnih konstruktora – tvorničkih funkcija koje vraćaju instancu izgrađenu na nezadani način.

2.23.1.3. @staticmethod

Definira metodu koja ne prima ni instancu ni klasu – to je samo obična funkcija koja iz organizacijskih razloga živi u imenskom prostoru klase:

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

Temperature.c_to_f(100)   # 212.0

Koristi ga štedljivo; ako funkcija stvarno nema nikakve veze sa stanjem klase, obična funkcija na razini modula obično je čišća.

2.23.2. Pisanje vlastitog dekoratora

Dekorator je funkcija koja prima funkciju i vraća funkciju. Minimalni oblik:

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)

Izlaz:

calling add

wrapper se zatvara nad func i sve mu prosljeđuje. *args / **kwargs omogućuje mu rad s bilo kojom funkcijom, ne samo s onima s dva argumenta. Ovaj obrazac temelj je razrađenijih dekoratora (mjerenje vremena, predmemoriranje, ponovni pokušaj pri neuspjehu), no srž je uvijek ista: primi funkciju, vrati funkciju.