2.23. Dekoratory¶
Dekorator to fragment składni, który opakowuje funkcję (lub metodę) w inną funkcję. Opakowanie może dodać zachowanie przed oryginalnym wywołaniem i po nim, zastąpić wartość zwracaną lub dołączyć metadane. Postać jest następująca:
@wrapper
def f():
...
Wiersz @wrapper jest równoważny f = wrapper(f) – funkcja f jest budowana normalnie, następnie przekazywana do wrapper, a wynik zostaje ponownie przypisany do nazwy f.
Dekorator przyjmuje funkcję i zwraca nową funkcję.¶
2.23.1. Wbudowane dekoratory metod¶
Kilka dekoratorów jest dostarczanych z Pythonem i jest używanych wewnątrz ciał klas.
2.23.1.1. @property¶
Zamienia metodę w obliczany atrybut. Wywołujący odwołuje się do niego tak, jakby był zwykłym atrybutem (bez nawiasów), ale przy każdym odczycie uruchamiana jest 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
Używaj tego, gdy atrybut, który wygląda na prosty, wymaga drobnego obliczenia w tle. Jeśli praca jest kosztowna, lepiej użyć zwykłej metody – wywołujący nie oczekują, że odczyty atrybutów będą wolne.
2.23.1.2. @classmethod¶
Definiuje metodę, która jako pierwszy argument otrzymuje klasę zamiast instancji. Pierwszy parametr jest zwyczajowo nazywany 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 klasowe to standardowy sposób na dostarczenie alternatywnych konstruktorów – funkcji fabrykujących, które zwracają instancję zbudowaną w niedomyślny sposób.
2.23.1.3. @staticmethod¶
Definiuje metodę, która nie otrzymuje ani instancji, ani klasy – jest to po prostu zwykła funkcja, która ze względów organizacyjnych żyje w przestrzeni nazw klasy:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Używaj tego oszczędnie; jeśli funkcja naprawdę nie ma nic wspólnego ze stanem klasy, zwykła funkcja na poziomie modułu jest zazwyczaj czytelniejsza.
2.23.2. Pisanie własnego dekoratora¶
Dekorator to funkcja, która przyjmuje funkcję i zwraca funkcję. Minimalna postać:
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)
Wynik:
calling add
wrapper domyka func i przekazuje mu wszystko. *args / **kwargs pozwala mu działać na dowolnej funkcji, nie tylko dwuargumentowej. Ten wzorzec jest podstawą bardziej rozbudowanych dekoratorów (mierzenie czasu, buforowanie, ponawianie po niepowodzeniu), ale rdzeń jest zawsze taki sam: przyjmij funkcję, zwróć funkcję.