目錄 | 上一節 (7.3 返回函數) | 下一節 (7.5 裝飾方法)python
本節介紹裝飾器(decorator)。由於這是一個高級主題,因此咱們只作簡單介紹。git
譯註:根據譯者我的的猜想,在《設計模式》(《 Design Patterns: Elements of Reusable Object-Oriented Software》)一書中寫到 decorator also known as wrapper
,因此下文提到包裝器(wrapper),其實說的就是裝飾器。這裏爲了保持和原文一致,因此翻譯的時候沒有將「包裝器」替換爲「裝飾器」。github
考慮這樣一個函數:segmentfault
def add(x, y): return x + y
考慮給 add(x, y)
函數添加日誌功能:設計模式
def add(x, y): print('Calling add') return x + y
也帶有日誌功能的 sub(x, y)
函數:app
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
使用該函數:this
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
該特殊語法執行與上面徹底相同的確切步驟。裝飾器只是一種新語法,用於裝飾函數。
對於裝飾器而言,還有許多比這裏展現的更微妙的細節,例如,在類裏面使用裝飾器,或者對同一個函數使用多個裝飾器。不過,這裏的例子已經很好地說明了如何使用它們。通常而言,它是對出如今各類函數定義中的重複代碼的響應。裝飾器能夠將重複代碼移至中心定義。
若是你定義了一個函數,那麼函數的名稱和函數所屬模塊的名稱會分別存儲到 __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)的診斷工具。