翻譯:《實用的Python編程》07_04_Function_decorators
目錄 | 上一節 (7.3 返回函數) | 下一節 (7.5 裝飾方法)
7.4 函數裝飾器
本節介紹裝飾器(decorator)。因為這是一個高級主題,所以我們只做簡單介紹。
譯注:根據譯者個人的猜測,在《設計模式》(《 Design Patterns: Elements of Reusable Object-Oriented Software》)一書中寫到 decorator also known as wrapper,所以下文提到包裝器(wrapper),其實說的就是裝飾器。這里為了保持和原文一致,所以翻譯的時候沒有將“包裝器”替換為“裝飾器”。
日志示例
考慮這樣一個函數:
def add(x, y):
return x + y
考慮給 add(x, y) 函數添加日志功能:
def add(x, y):
print('Calling add')
return x + y
也帶有日志功能的 sub(x, y)函數:
def sub(x, y):
print('Calling sub')
return x - y
觀察
觀察: 這是一種重復。
在有大量重復代碼的地方編寫程序通常很煩人。這些代碼不僅寫起來枯燥,維護起來也很麻煩。尤其是你決定更改其工作方式的時候(例如,可能是另一種類型的日志記錄)。
記錄日志的代碼
也許你可以創建一個添加了日志功能的函數。例如包裝器(wrapper):
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
使用該函數:
def add(x, y):
return x + y
logged_add = logged(add)
當調用 logged 返回的函數時會發生什么?
logged_add(3, 4) # You see the logging message appear
此示例闡明了創建所謂的包裝器函數(wrapper function) 的過程。
包裝器是一個函數,它包裝了另一個帶有額外處理功能的函數,但在其它方面與原始函數的工作方式完全相同。
>>> logged_add(3, 4)
Calling add # Extra output. Added by the wrapper
7
>>>
注意事項:logged() 函數創建了一個包裝器,并作為結果返回。
裝飾器
在 Python 中,在函數中使用包裝器非常常見。因為如此普遍,所以有一個特殊的語法。
def add(x, y):
return x + y
add = logged(add)
# Special syntax
@logged
def add(x, y):
return x + y
該特殊語法執行與上面完全相同的確切步驟。裝飾器只是一種新語法,用于裝飾函數。
說明
對于裝飾器而言,還有許多比這里展示的更微妙的細節,例如,在類里面使用裝飾器,或者對同一個函數使用多個裝飾器。不過,這里的例子已經很好地說明了如何使用它們。一般而言,它是對出現在各種函數定義中的重復代碼的響應。裝飾器可以將重復代碼移至中心定義。
練習
練習 7.10:計時裝飾器
如果你定義了一個函數,那么函數的名稱和函數所屬模塊的名稱會分別存儲到 __name__ 和 __module__屬性中。示例:
>>> def add(x,y):
return x+y
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>>
請創建 timethis.py 文件,并在文件中編寫 timethis(func) 函數。timethis(func) 函數包裝一個具有額外邏輯層的函數,邏輯層打印出函數執行所需要的事件。為此,你將在函數中添加如下計時調用。
start = time.time()
r = func(*args,**kwargs)
end = time.time()
print('%s.%s: %f' % (func.__module__, func.__name__, end-start))
(timethis(func))裝飾器工作方式示例:
>>> from timethis import timethis
>>> @timethis
def countdown(n):
while n > 0:
n -= 1
>>> countdown(10000000)
__main__.countdown : 0.076562
>>>
討論:@timethis 裝飾器可以放在任何函數的前面,即你應該把裝飾器用作性能調優(performance tuning)的診斷工具。
浙公網安備 33010602011771號