2.23. Dekoratorer#
En dekorator är en bit syntax som lindar in en funktion (eller en metod) i en annan funktion. Inlindaren kan lägga till beteende före och efter det ursprungliga anropet, ersätta returvärdet eller bifoga metadata. Formen är:
@wrapper
def f():
...
Raden @wrapper är likvärdig med f = wrapper(f) – funktionen f byggs normalt, lämnas sedan över till wrapper, och resultatet binds om till namnet f.
En dekorator tar in en funktion och returnerar en ny funktion.#
2.23.1. Inbyggda metoddekoratorer#
Några få dekoratorer levereras med Python och används inuti klasskroppar.
2.23.1.1. @property#
Förvandlar en metod till ett beräknat attribut. Anroparen kommer åt det som om det vore ett vanligt attribut (inga parenteser), men en metod körs vid varje läsning:
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
Använd det när ett attribut som ser enkelt ut behöver en liten beräkning bakom sig. Om arbetet är dyrt, föredra en vanlig metod – anropare förväntar sig inte att attributläsningar ska vara långsamma.
2.23.1.2. @classmethod#
Definierar en metod som tar emot klassen som sitt första argument i stället för en instans. Den första parametern namnges enligt konvention 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()
Klassmetoder är standardsättet att tillhandahålla alternativa konstruktorer – fabriksfunktioner som returnerar en instans byggd på ett icke-standardmässigt sätt.
2.23.1.3. @staticmethod#
Definierar en metod som varken tar emot instansen eller klassen – den är bara en vanlig funktion som bor i klassens namnrymd av organisatoriska skäl:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Använd det sparsamt; om en funktion verkligen inte har något med klassens tillstånd att göra är en vanlig funktion på modulnivå oftast renare.
2.23.2. Att skriva en egen dekorator#
En dekorator är en funktion som tar en funktion och returnerar en funktion. Minimiformen:
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)
Utmatning:
calling add
wrapper sluter över func och vidarebefordrar allt till den. *args / **kwargs låter den fungera på vilken funktion som helst, inte bara sådana med två argument. Det här mönstret är grunden för mer utarbetade dekoratorer (tidtagning, cachning, försök-igen-vid-fel) men kärnan är alltid densamma: ta in en funktion, returnera en funktion.