2.23. Koristelijat

Koristelija on syntaksin pala, joka kietoo funktion (tai metodin) toiseen funktioon. Kääre voi lisätä käyttäytymistä ennen alkuperäistä kutsua ja sen jälkeen, korvata paluuarvon tai liittää metatietoja. Muoto on:

@wrapper
def f():
    ...

@wrapper-rivi vastaa lauseketta f = wrapper(f) – funktio f rakennetaan normaalisti, sitten luovutetaan funktiolle wrapper, ja tulos sidotaan uudelleen nimeen f.

Funktio menee "kääre"-laatikkoon vasemmalla ja tulee ulos oikealla koristeltuna funktiona.

Koristelija ottaa funktion sisään ja palauttaa uuden funktion.

2.23.1. Sisäänrakennetut metodikoristelijat

Muutama koristelija tulee Pythonin mukana ja niitä käytetään luokkarunkojen sisällä.

2.23.1.1. @property

Muuttaa metodin lasketuksi attribuutiksi. Kutsuja käyttää sitä kuin se olisi tavallinen attribuutti (ei sulkeita), mutta metodi suoritetaan jokaisella lukukerralla:

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

Käytä sitä, kun attribuutti, joka näyttää yksinkertaiselta, tarvitsee pienen laskennan taustalleen. Jos työ on kallista, suosi tavallista metodia – kutsujat eivät odota attribuuttilukujen olevan hitaita.

2.23.1.2. @classmethod

Määrittelee metodin, joka saa luokan ensimmäisenä argumenttinaan instanssin sijaan. Ensimmäinen parametri nimetään sopimuksen mukaan nimellä 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()

Luokkametodit ovat vakiotapa tarjota vaihtoehtoisia konstruktoreita – tehdasfunktioita, jotka palauttavat ei-oletustavalla rakennetun instanssin.

2.23.1.3. @staticmethod

Määrittelee metodin, joka ei saa instanssia eikä luokkaa – se on vain tavallinen funktio, joka elää luokan nimiavaruudessa järjestelysyistä:

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

Temperature.c_to_f(100)   # 212.0

Käytä sitä säästeliäästi; jos funktiolla ei todella ole mitään tekemistä luokan tilan kanssa, tavallinen moduulitason funktio on yleensä siistimpi.

2.23.2. Mukautetun koristelijan kirjoittaminen

Koristelija on funktio, joka ottaa funktion ja palauttaa funktion. Vähimmäismuoto:

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)

Tuloste:

calling add

wrapper sulkeutuu func-funktion ympärille ja välittää kaiken sille. *args / **kwargs antaa sen toimia minkä tahansa funktion kanssa, ei vain kaksiargumenttisten. Tämä malli on perusta monimutkaisemmille koristelijoille (ajanotto, välimuistitus, uudelleenyritys epäonnistuessa), mutta ydin on aina sama: ota funktio sisään, palauta funktio ulos.