2.23. Decorators¶
Een decorator is een stukje syntaxis dat een functie (of een methode) in een andere functie wikkelt. De wrapper kan gedrag toevoegen voor en na de oorspronkelijke aanroep, de retourwaarde vervangen, of metadata aanhechten. De vorm is:
@wrapper
def f():
...
De regel @wrapper is equivalent aan f = wrapper(f) – de functie f wordt normaal gebouwd, vervolgens aan wrapper overhandigd, en het resultaat wordt opnieuw aan de naam f gebonden.
Een decorator neemt een functie in en geeft een nieuwe functie terug.¶
2.23.1. Ingebouwde methode-decorators¶
Een paar decorators worden met Python meegeleverd en worden binnen klasse-bodies gebruikt.
2.23.1.1. @property¶
Verandert een methode in een berekend attribuut. De aanroeper benadert het alsof het een gewoon attribuut is (geen haakjes), maar bij elke lezing draait er een methode:
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
Gebruik het wanneer een attribuut dat er eenvoudig uitziet, een kleine berekening erachter nodig heeft. Als het werk duur is, verkies dan een gewone methode – aanroepers verwachten niet dat het lezen van attributen traag is.
2.23.1.2. @classmethod¶
Definieert een methode die de klasse als eerste argument ontvangt in plaats van een instance. De eerste parameter heet conventioneel 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()
Klassemethoden zijn de standaardmanier om alternatieve constructors te bieden – fabrieksfuncties die een instance teruggeven die op een niet-standaard manier is gebouwd.
2.23.1.3. @staticmethod¶
Definieert een methode die noch de instance noch de klasse ontvangt – het is gewoon een gewone functie die om organisatorische redenen in de naamruimte van de klasse leeft:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Gebruik het spaarzaam; als een functie echt niets met de toestand van de klasse te maken heeft, is een gewone functie op moduleniveau meestal schoner.
2.23.2. Een aangepaste decorator schrijven¶
Een decorator is een functie die een functie neemt en een functie teruggeeft. De minimale vorm:
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)
Uitvoer:
calling add
De wrapper sluit zich rond func en stuurt alles ernaartoe door. *args / **kwargs laat hem op elke functie werken, niet alleen op functies met twee argumenten. Dit patroon is de basis van uitgebreidere decorators (timing, caching, retry-on-failure) maar de kern is altijd hetzelfde: neem een functie in, geef een functie terug.