2.23. Dekorátorok¶
A dekorátor egy olyan szintaxiselem, amely egy függvényt (vagy egy metódust) becsomagol egy másik függvénybe. A becsomagoló viselkedést adhat hozzá az eredeti hívás előtt és után, lecserélheti a visszatérési értéket, vagy metaadatot csatolhat hozzá. Az alakja:
@wrapper
def f():
...
A @wrapper sor egyenértékű az f = wrapper(f) kifejezéssel – az f függvény normálisan épül fel, majd átadódik a wrapper függvénynek, az eredmény pedig újraköttetik az f névhez.
Egy dekorátor bevesz egy függvényt, és visszaad egy új függvényt.¶
2.23.1. Beépített metódusdekorátorok¶
Néhány dekorátor a Pythonnal együtt érkezik, és osztálytörzseken belül használatos.
2.23.1.1. @property¶
Egy metódust számított attribútummá alakít. A hívó úgy fér hozzá, mintha egyszerű attribútum lenne (zárójelek nélkül), de minden olvasáskor lefut egy metódus:
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
Akkor használd, amikor egy egyszerűnek tűnő attribútum mögött egy apró számításra van szükség. Ha a munka költséges, inkább egy szokásos metódust válassz – a hívók nem számítanak arra, hogy az attribútumolvasás lassú legyen.
2.23.1.2. @classmethod¶
Olyan metódust definiál, amely első argumentumként egy példány helyett magát az osztályt kapja meg. Az első paramétert szokás szerint cls névvel látják el:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def origin(cls):
return cls(0, 0)
p = Point.origin()
Az osztálymetódusok a szabványos módja az alternatív konstruktorok biztosításának – olyan gyártófüggvények, amelyek egy nem alapértelmezett módon felépített példányt adnak vissza.
2.23.1.3. @staticmethod¶
Olyan metódust definiál, amely sem a példányt, sem az osztályt nem kapja meg – ez csupán egy egyszerű függvény, amely szervezési okokból él az osztály névterében:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
Takarékosan használd; ha egy függvénynek valójában semmi köze az osztály állapotához, általában tisztább egy egyszerű modulszintű függvény.
2.23.2. Egyedi dekorátor megírása¶
A dekorátor egy olyan függvény, amely bevesz egy függvényt, és visszaad egy függvényt. A minimális alakja:
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)
Kimenet:
calling add
A wrapper lezárja a func függvényt, és mindent továbbít hozzá. A *args / **kwargs lehetővé teszi, hogy bármely függvényen működjön, ne csak kétargumentumosakon. Ez a minta az alapja a kidolgozottabb dekorátoroknak (időmérés, gyorsítótárazás, hiba esetén újrapróbálkozás), de a lényeg mindig ugyanaz: vegyél be egy függvényt, adj vissza egy függvényt.