裝飾器本質上就是一個Python函數,它可讓其它函數在不須要作任何代碼變更的前提下,增長額外的功能,裝飾器的返回值也是一個函數對象。python
裝飾器的應用場景:好比插入日誌,性能測試,事物處理,緩存等等場景。緩存
如今我有一個需求,我想讓你在不改變函數代碼的狀況下,測試出這個函數的執行時間:app
import time def func1(): print("in func1") def timer(func): def inner(): start = time.time() func() print(time.time() - start) return inner func1 = timer(func1) func1()
可是若是有多個函數,我都想讓你測試他們的執行時間,你豈不是每一個函數都要寫一遍func1 = timer(func1)?ide
這樣寫很是麻煩,由於這些函數的函數名都是不相同的,有func一、func2,func3等等。函數
針對這種狀況,python給咱們提供了一個簡單快捷的方法,那就是語法糖。性能
1 import time 2 3 4 def timer(func): 5 def inner(): 6 start = time.time() 7 func() 8 print(time.time() - start) 9 return inner 10 11 12 @timer # ==> func1 = timer(func1) 13 def func1(): 14 print("in func1") 15 16 17 func1()
剛剛咱們討論的裝飾器都是裝飾不帶參數的函數,如今要裝飾一個帶參數的函數要怎麼辦呢?測試
1 import time 2 3 def timer(func): 4 def inner(a): 5 start = time.time() 6 func(a) 7 print(time.time() - start) 8 return inner 9 10 11 @timer 12 def func1(a): 13 print(a) 14 15 func1(1)
1 import time 2 3 4 def timer(func): 5 def inner(*args, **kwargs): 6 start = time.time() 7 result = func(*args, **kwargs) 8 print(time.time() - start) 9 return result 10 return inner 11 12 13 @timer #==> func1 = timer(func1) 14 def func1(a, b): 15 print("in func1") 16 17 18 @timer #==> func2 = timer(func2) 19 def func2(a): 20 print("in func2 and get a:%s" % a) 21 return "func2 end" 22 23 24 func1("aaa", "bbb") 25 print(func2("aa"))
上面的裝飾器已經很是完美了,可是咱們在正常狀況下查看函數信息的方法卻在此處所有失效了:this
def index(): """ 這是一個主頁信息 :return: """ print("from index") print(index.__doc__) # 查看函數註釋的方法 print(index.__name__) # 查看函數名的方法
那麼如何解決這個問題呢?spa
from functools import wraps def deco(func): @wraps(func) # 放在最內層函數最上方 def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper @deco def index(): """ 顯示首頁信息 :return: """ print("from index") print(index.__doc__) print(index.__name__)
裝飾器是完美的遵循了這個開放封閉原則的。設計
那麼什麼是開放封閉原則呢?
咱們能夠從下面兩個方面來看。
爲何要對擴展開放呢?
咱們說,任何一個程序,不可能在設計之初就已經想好了全部的功能,而且將來不作任何更新和修改。因此咱們必須容許代碼擴展、添加新功能。
爲何要對修改封閉呢?
就像咱們剛剛提到的,由於咱們寫的一個函數,頗有可能已經交付給其餘人使用了,若是這個咱們對其進行了修改,頗有可能影響其餘已經在使用該函數的用戶。
def timer(func): def inner(*args, **kwargs): """執行函數以前要作的""" result = func(*args, **kwargs) """執行函數以後要作的""" return result return inner
1 from functools import wraps 2 3 def deco(func): 4 @wraps(func) # 加在最內層函數最上方 5 def wrapper(*args, **kwargs): 6 return func(*args, **kwargs) 7 return wrapper
假如你有成千上萬個函數使用了一個裝飾器,如今你想把這些裝飾器都取消掉,你要怎麼作?
若是一個一個的取消,那任務量也太大了吧。
萬一,沒過幾天,你又須要用這些裝飾器,豈不是要吐血。
那麼解決辦法,就是在裝飾器上加上參數:
1 def wrapper_out(flag): 2 def wrapper(func): 3 @wraps(func) 4 def inner(*args, **kwargs): 5 if flag: 6 print("執行函數以前要作的") 7 8 result = func(*args, **kwargs) 9 10 if flag: 11 print("執行函數以後要作的") 12 13 return result 14 return inner 15 return wrapper 16 17 18 @wrapper_out(False) # 經過傳遞True和False來控制裝飾器內部的運行效果 19 def func(): 20 pass 21 22 23 func()
先執行下面這樣一個代碼
1 def wrapper1(func): 2 def inner(*args, **kwargs): 3 print("111") 4 result = func(*args, **kwargs) 5 print("222") 6 return result 7 return inner 8 9 10 def wrapper2(func): 11 def inner(*args, **kwargs): 12 print("333") 13 result = func(*args, **kwargs) 14 print("444") 15 return result 16 return inner 17 18 19 @wrapper2 20 @wrapper1 21 def func(): 22 print("this is func") 23 24 func()
當執行完畢後,能夠看到執行結果爲:
333 111 this is func 222 444
執行順序:首先@warpper1裝飾器來,而後獲取到一個新函數是wrapper1中的inner,而後執行@wrapper2。這個時候,wrapper2裝飾的就是wrapper1中的inner了。
因此,執行順序就像:第二層裝飾器前(第一層裝飾器前(目標)第一層裝飾器後)第二層裝飾器後。程序從左到右執行起來,這就是咱們看到的結果。