@wraps 修飾器:讓你的 Python 代碼更加簡短可愛 | 從簡單實例來認識它
我們在上一篇文章(Python實例來認識并發與并行)中用到了 @timer ,在函數定義時,加上一個小小的 @timer ,這樣,函數執行結束后,就會自動在控制臺匯報自己運行的時間。
比如下面這樣:
- @timer
- def piper():
- for i in range(10000):
- i = i * i ** 10
- piper()
- 輸出:
- timer: using 0.00600 s
實際上,這個計時器邏輯 @timer 是我們自己用 Python 中的修飾器特性[1]來實現的。
拆解邏輯
其實我們不用修飾器,自己也能實現計時的邏輯。
- def piper():
- for i in range(10000):
- i = i * i ** 10
- t = time.time() # 記錄函數開始時時間
- piper()
- print(f"timer: using {time.time() - t :.5f} s") # 獲取函數運行時間并打印
注意到我們執行函數時,在其上下都包裹上了邏輯。如果我們希望函數自帶計時邏輯,那么為了包住原函數,只能去新定義一個函數。
- def time_wrapper(func):
- # func 是一個函數
- t = time.time()
- func()
- print(f"timer: using {time.time() - t :.5f} s")
- time_wrapper(piper)
- 輸出:
- timer: using 0.00600 s
我們想測試某一個函數運行時間時,將函數名輸入到 time_wrapper 里面就好。
更優雅的改進
上述代碼顯然有缺點:
- 我們在編程時,心智負擔增大了;此外,代碼更冗長了
- 如果我們只是希望函數新增一個功能,顯然用 time_wrapper 是不行的,因為其并沒有改變 piper 本身
于是我們請出今天的主角 修飾器@wraps[2] 。
還用我們的 timer 舉例子,我們讓所有在 @timer 下的函數,都經過如下處理:
- def timer(func):
- @wraps(func)
- def inner_func():
- t = time.time()
- rts = func()
- print(f"timer: using {time.time() - t :.5f} s")
- return rts
- return inner_func
以 piper 為例,我們經歷了如下變化。
- @timer
- def 原始piper():
- for i in range(10000):
- i = i * i ** 10
實際上,當你再調用 piper 時,你的 piper 內部邏輯早已變為:
- def 當前piper():
- t = time.time()
- rts = 原始piper()
- print(f"timer: using {time.time() - t :.5f} s")
- return rts
總結
本文簡單與讀者朋友們「科普」一下修飾器,注意到我們這里實際上僅僅修飾了無參數的函數。其實,修飾器還有許多更加優雅用途,比如傳入參數 *args, **kwargs ,修飾類 __call__ 等用法。期待以后我遇到好的應用場景,將經驗分享給朋友們。