代碼環境:python3.6
裝飾器是 python 的一種語法糖,本質是一個可調用的對象,其參數是一個被裝飾的函數。裝飾器可能會處理被裝飾的函數並返回,或者將其替換成另外一個函數或對象。python
這種用法出如今不少 python web 框架中,例如把 url 地址映射到 http 響應的函數上的註冊處。web
下面舉個簡單的例子:閉包
def register(func): # 內部無新函數 print('running register {}'.format(func)) return func @register def my_func(): print('running my_func()') if __name__ == "__main__": my_func()
大多數裝飾器,一般內部會定義一個閉包結構的函數,將其返回替換被裝飾函數。app
這種用法最多見,用於不修改原函數的基礎上增長額外的功能,好比計算函數的運行時間、輸出指定格式日誌等。框架
下面用一個裝飾器輸出函數運行時間:函數
def running_time(func): # 內部有新函數 def print_running_time(*args): '''打印函數運行時間''' t0 = time.time() result = func(*args) need_time = time.time() - t0 print('新列表生成時間(秒):{:.8f}'.format(need_time)) return result return print_running_time @running_time def new_list(n): '''生成一個新列表''' temp_list = [] for x in range(n): temp_list.append(x * (x + 1)) return temp_list if __name__ == "__main__": print('新列表長度:{}'.format(len(new_list(12345)))) print('new_list 函數的 __name__ 屬性:{}'.format(new_list.__name__)) print('new_list 函數的 __doc__ 屬性:{}'.format(new_list.__doc__))
執行結果:url
running register <function my_func at 0x0000000002889B70> 新列表生成時間(秒):0.00250006 新列表長度:12345 new_list 函數的 __name__ 屬性:print_running_time new_list 函數的 __doc__ 屬性:打印函數運行時間
從上述例子中咱們注意到,在調用new_list(12345)
打印出結果以前,結果欄先輸出了裝飾器中的print
語句,這說明:日誌
裝飾器在導入模塊@func
時當即執行。code
在上述結果中,咱們還注意到另外一個特色:new_list
函數的__name__
和__doc__
屬性都被替換成裝飾器內部函數的相關屬性。因此,咱們須要改進上面的例子,使用functools.wraps
裝飾器把相關屬性從func
複製到新函數中。orm
改進例子以下:
from functools import wraps def running_time(func): # 內部有新函數 @wraps(func) # 此處使用 wraps 裝飾器 def print_running_time(*args): '''打印函數運行時間''' t0 = time.time() result = func(*args) need_time = time.time() - t0 print('新列表生成時間(秒):{:.8f}'.format(need_time)) return result return print_running_time @running_time def new_list(n): '''生成一個新列表''' temp_list = [] for x in range(n): temp_list.append(x * (x + 1)) return temp_list if __name__ == "__main__": print('新列表長度:{}'.format(len(new_list(12345)))) print('new_list 函數的 __name__ 屬性:{}'.format(new_list.__name__)) print('new_list 函數的 __doc__ 屬性:{}'.format(new_list.__doc__))
執行結果:
running register <function my_func at 0x0000000002879B70> 新列表生成時間(秒):0.00250006 新列表長度:12345 new_list 函數的 __name__ 屬性:new_list new_list 函數的 __doc__ 屬性:生成一個新列表
觀察改進後例子的運行結果,new_list
函數的相關屬性已恢復正常。