裝飾器
裝飾器形成的過程:最簡單的裝飾器——有返回值的——有一個引數——萬能引數
裝飾器的作用:不想修改函式的呼叫方式 但是還想在原來的函式前後新增功能
原則:開放封閉原則
語法糖:@裝飾器函式名
裝飾器的固定模式
原則:開放封閉原則
開放:對擴充套件是開放的
封閉:對修改是封閉的
首先我們來逐步實現這麼一個功能吧
計算程式碼執行的時間
#先來看下需要的元件 import time #time 模組 time.time() #獲取當前時間 time.sleep(5) #讓程式睡眠多少時間 print(time.time()) #來試下
我們來寫一個可以計算時間的函式
def func(): start=time.time() time.sleep(0.1) #時間太短 系統會顯示 0.0,所以我們讓他休眠 0.1s print("計算程式執行的時間") end=time.time() print(end-start) func()
但是這裡有一個問題:如果要計算很多函式的執行時間,那不是要在函式中都加上幾行計算的程式碼,這顯然是不可行的,一般寫好的沒問題的函式,是不會去對它進行修改,而且還很麻煩
所以這裡我們寫一個計算時間的函式獨立出來
def times(f): start=time.time() time.sleep(0.1) f() end=time.time() print(end-start) def func(): print("計算程式執行的時間") def func2(): print(time.sleep(5)) times(func) times(func2)
但是這樣貌似還有一個問題,我要計算時間的函式都要呼叫 times() 函式,這樣其實是很麻煩的
最終還是要呼叫 func() 就可以計算執行時間,就是說你呼叫的是 func() 其實呼叫的 times() 這就很完美了
接著我們來了解下裝飾器吧,解決這樣的問題
先來寫一個簡單的裝飾器
#一個簡單的裝飾器 import time def func(): time.sleep(0.1) print(`你好,世界`) def timmer(f): #裝飾器函式 def inner(): start = time.time() f() #被裝飾的函式 end = time.time() print(end - start) return inner func = timmer(func) #通過這一步就可以使用 func 呼叫 timmer 了 func()
我們這裡做的事情就是:在不想修改函式的呼叫方式的情況下 但還想在原來的函式前後新增功能
接著我們來看另一個甜甜的東西——語法糖
import time def timmer(f): #裝飾器函式 def inner(): start = time.time()
time.sleep(0.1) f() #被裝飾的函式 end = time.time() print(end - start) return inner @timmer #語法糖 替代下面 func = timmer(func) 這句話 def func(): time.sleep(0.1) print(`你好,世界`) #func = timmer(func) #通過這一步就可以使用 func 呼叫 timmer 了 func()
然後我們來看接收返回值的裝飾器
import time def timmer(f): #裝飾器函式 def inner(): start=time.time() time.sleep(0.1) ret = f() #被裝飾的函式 end=time.time() print(end-start) return ret #接收 func() 函式的返回值 return inner #這個地方要穿函式的名字 而不是加括號 @timmer #語法糖 替代下面 func = timmer(func) 這句話 def func(): print(`你好,世界`) return `Hello` print(func()) #返回值列印 返回值為:Hello
最後我們來看帶引數的裝飾器,一個引數與萬能引數是一樣的,只是引數的不同罷了
#一個引數的裝飾器 import time def timmer(f): #裝飾器函式 def inner(a): #3.給 inner() 加上引數 start=time.time() time.sleep(0.1) ret = f(a) #4.由於 inner() 把引數傳給了 f(),所以 f() 也要加上引數,最後測試傳值 end=time.time() print(end-start) return ret #接收 func() 函式的返回值 return inner @timmer #語法糖 替代下面 func = timmer(func) 這句話 def func(a): #2.這裡也要加上引數,由於執行 func 時,實際執行的 inner(),所以 inner() 也要加上引數 print("計算程式執行的時間",a) return `Hello` print(func("123")) #1.加入引數 #輸出結果 ``` 計算程式執行的時間 123 0.10937380790710449 Hello ```
###萬能引數的裝飾器 #可以看出 只是在 a 的位置更換了 *args **kwargs 來接收引數 import time def timmer(f): def inner(*args,**kwargs): start=time.time() time.sleep(0.1) ret = f(*args,**kwargs) end=time.time() print(end-start) return ret return inner @timmer def func(*args,**kwargs): print("計算程式執行的時間",args,kwargs) #2. 就是這個位置的 kwargs,如果寫了,列印時會出現一個 ` {} ` 空字典,並不會報錯 return `Hello` print(func(`1`,`2`,`3`,k=100)) #1. 不需要關鍵字傳參時,kwargs 可以不寫 #輸出結果 ``` 計算程式執行的時間 (`1`, `2`, `3`) {`k`: 100} 0.1093745231628418 Hello ```
好了,以上就是我們對於裝飾器完善的最終結果了
裝飾器的固定模式
def wrapper(f): #裝飾器函式,f是被裝飾的函式 def inner(*args,**kwargs): #定義一個內建函式,且和下面統級別的返回值名字一致且不加括號 #動態引數一定要原封不動的傳給 被裝飾的函式 f() ```在被裝飾函式之前要做的事``` ret = f(*args,**kwargs) #被裝飾的函式 函式執行完的返回值要原封不動傳給 return ```在被裝飾函式之後要做的事``` return ret return inner @wrapper #語法糖 @裝飾器函式名 等價於 func = wrapper(func) def func(a,b): #被裝飾的函式 time.sleep(0.01) print(`你好:世界`,a,b) return `新年好` #print(func(23,45)) #接收返回值 ret = func(23,45) #接收返回值 print(ret)