2.23. 裝飾器¶
裝飾器 是一種語法,它將一個函式(或方法)包裹在另一個函式中。包裹器可以在原始呼叫前後新增行為、替換回傳值,或附加中介資料。其形式為:
@wrapper
def f():
...
@wrapper 這一行等同於 f = wrapper(f) -- 函式 f 先正常建立,接著交給 wrapper,其結果再重新繫結到名稱 f。
裝飾器接收一個函式,並回傳一個新的函式。¶
2.23.1. 內建的方法裝飾器¶
Python 內建了少數幾個裝飾器,用於類別主體中。
2.23.1.1. @property¶
將方法轉變為 計算屬性。呼叫者像存取普通屬性一樣存取它(不加括號),但每次讀取時都會執行一個方法:
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
當某個 看起來 簡單的屬性背後需要進行少量計算時,可使用它。如果這項工作很耗費資源,則改用一般方法 -- 呼叫者不會預期讀取屬性會很慢。
2.23.1.2. @classmethod¶
定義一個以 類別 而非實例作為第一個引數的方法。第一個參數依慣例命名為 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()
類別方法是提供 替代建構子 的標準方式 -- 也就是以非預設方式建立並回傳實例的工廠函式。
2.23.1.3. @staticmethod¶
定義一個既不接收實例也不接收類別的方法 -- 它只是一個基於組織考量而存在於類別命名空間中的普通函式:
class Temperature:
@staticmethod
def c_to_f(c):
return c * 9 / 5 + 32
Temperature.c_to_f(100) # 212.0
請審慎使用;如果某個函式其實與類別的狀態毫無關係,使用普通的模組層級函式通常更為清楚。
2.23.2. 撰寫自訂裝飾器¶
裝飾器是一個接收函式並回傳函式的函式。最精簡的形式:
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)
輸出:
calling add
wrapper 對 func 形成閉包,並將一切轉發給它。*args / **kwargs 讓它能作用於任何函式,而不僅限於只有兩個引數的函式。這種模式是更複雜裝飾器(計時、快取、失敗重試)的基礎,但其核心始終相同:接收一個函式,回傳一個函式。