裝飾器顧名思義就是一個有裝飾功能的工具,那麼裝飾器又是用來裝飾什麼的?爲何要裝飾這個東西?裝飾的目的是什麼呢?本文會一一做答讓小弟一個一個說web
談及裝飾器就要引伸一個概念,那就是開放封閉原則,那麼問題又來了 什麼是開放封閉……好好好,直接說這個,開放封閉原本是兩個對立的概念,也就是說是一對反義詞,那麼爲何要提出開放封閉原則呢?緣由是在平常的開發工做中,通常最初上線的產品的功能是不盡完善的,就是說不夠完美但已經可以支撐平常使用,其他的功能能夠往後擴展,舉例:N年前的QQ和如今的QQ(PS:雖然如今本人不怎麼用了)。在後期擴展功能時,由於函數已是寫好的,並且存在大量調用,因此要直接去給函數增添新的功能顯然不現實,因此咱們只能新建函數去給原來的函數擴展功能(其實這就是一個裝飾器啦)數據庫
那麼開放封閉原則究竟是什麼? 答案是對源碼封閉,對新功能開放閉包
封閉原則:不要改變源代碼app
開放原則:能增長一些額外的功能函數
若是還有客觀對開放封閉原則似知似解的話,不要緊接着往下看,不影響您食用本文,由於Python裝飾器自己就是對開放封閉原則完美的詮釋工具
不低調的說,裝飾器就是一個函數,名字原本很高大上,但本質就是一個函數,裝飾器函數的功能就是要裝飾一個函數,在不改變被裝飾函數的源代碼及調用方式的前提下,爲其增長額外的功能。是否是有點門道了,是否是以爲這玩意也沒啥啦,真是優秀的同窗。網站
def warpper(f): #定義一個函數(裝飾器),傳入的參數是被修飾的函數的函數名
def inner(*args,**kwargs): #嵌套一個內存函數,這個函數主題纔是執行被裝飾函數源碼的關鍵
'''這塊能夠加要在被裝飾函數執行以前的操做哈'''
ret = f(*args,**kwargs) #這裏的形參我會在下面說明
'''這裏能夠寫被裝飾函數以後的,兄嘚,別客氣,想加啥方法加什麼'''
return ret #這裏的返回值若是我一下子不忘的話也會在下面說明
return inner
@warpper #這個叫語法糖,嗯……能夠吃(可能老外命名的時候就是這麼想的),結構是@加函數名,做用下面會說明
def func():
print('我就是那個被裝飾的函數')
由於本人比較懶,因此原諒我直接把代碼甩上去了,後面有註釋,看懂了的大佬能夠say goodbye啦,想打個人接着聽我白話,那我就把備註再重複一遍,哈哈,你也看到了,裝飾器用到了函數的嵌套(再具體點就是閉包,要問什麼是閉包,百度吧,哈哈),首先外層函數接收到一個函數名,而後返回值是內層函數的函數名;再來內層函數能夠接收參數,其中ret = f(*args,**kwargs)
有兩個做用,一是執行了傳入的函數,也就是執行了被修飾的函數,二是將返回值賦給了一個變量,此處要說明一下,對被修飾的函數功能的擴展要寫在這裏哦,最後 ret做爲內層函數的返回值返回給函數執行者。ui
@warpper
這東西和 func = wrapper(func)是同樣的,也就是說最後三行代碼能夠這樣寫spa
#@warpper
def func():
print('我就是那個被裝飾的函數')
func = wrapper(func) #注意奧,這玩意要寫在被裝飾函數的下邊,語法糖才寫上邊
至於爲何要寫着東西,或者爲何要用語法糖?請聽下回分解~~~,收起你滴拳頭,是這樣,我剛開始的時候談到了,裝飾器是要在不改變源碼和其調用方式的前提下給其增長新的功能,注意到了麼 調用方式 嗯……沒錯就是調用方式,若是我不這樣寫那我是否是要wrapper(func)()
這樣去調用啊,是否是有點繞了,可是我把wrapper(func)
賦值給了一個和被裝飾函數同名的變量,那我此時要怎麼調用,是否是就是func()
,這樣就知足了開放封閉原則,完美!其實本質就是要把裝飾器「假裝」成原函數,包括調用方式、參數、返回值,裝就要裝的像一點,對吧。code
def wrapper(f):
def inner(*args,**kwargs):
f(*args,**kwargs)
return inner
@wrapper
def func(a,b):
print(a,b)
函數func中有兩個形參a和b,說一下這兩個參數在裝飾器中的旅程,函數inner的萬能參數接收到a和b打包,而後inner函數充當中間商將打包後的元組給了f,在f中打散又成了變量a和b,在裝飾器時就不會影響傳參了,這就是裝飾帶參數的函數
裝飾有返回值的函數
def wrapper(f):
def inner():
ret = f()
return ret
return inner
哈哈,這段代碼中有個人心聲,大家都懂的,很簡單,裝飾有返回值的參數,在inner函數中將f()賦值給ret,這樣ret就接收到了返回值,再將ret返回給inner(),以此來達到「模擬」被裝飾函數的返回值,也能夠說是經過這種方法來拿到被裝飾函數的返回值。
先舉個例子,當咱們須要寫一個簡單的登錄認證功能的時候,咱們的目的是要用裝飾器給調用的函數增長一個認證是否登錄過此網站的功能,也就是要驗證用戶名和密碼,但好比不一樣公司的網站數據庫確定並不是同一個,這個時候就須要帶參數的裝飾器啦,它能夠實現咱們要用一個裝飾器裝飾多個相似函數的目的
def wrapper_out(n):
def wrapper(f):
def inner(*args,**kwargs):
with open(n,encoding='utf-8') as f1:
'''此部分省略認證的詳細功能'''
f(*args,**kwargs)
return inner
return wrapper
若是使用標準的裝飾器函數的話只能裝飾其中的一個函數,當裝飾另外一個函數時會由於訪問不到正確的數據庫而報錯。
@wrapper_out('webpage1')
這段代碼先執行wrapper_out('webpage1')
這個函數先把參數webpage傳給n,而且返回一個wrapper,此時@和wrapper結合在一塊有沒有很眼熟,沒錯,這就是咱們所熟悉的標準的裝飾器了,餘下的流程就和標準的裝飾器徹底相同了。
這是一種特殊的狀況,下面重點分析這種狀況的結果是如何產生的,會有點繞。
def wrapper1(func1):
def inner1():
print('wrapper1 ,before func')
func1()
print('wrapper1 ,after func')
return inner1
def wrapper2(func2): # func2 == inner1
def inner2():
print('wrapper2 ,before func')
func2() # inner1
print('wrapper2 ,after func')
return inner2
@wrapper2
@wrapper1
def f():
print('in f') # 3
f()
結果:
wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func
怎麼說?是否是和你預期的結果有所不一樣,下面來按步驟說一下爲何會產生這樣的結果(我儘可能表達清楚哈)
1. 函數定義不調用不執行直接pass
2. @wrapper2
@wrapper1
def f():
單獨看下面這兩行,標準裝飾器哈 @wrapper1 等價於 f = wrapper1(f) = inner1(返回值哈)
3. @wrapper2
@wrapper1 #注意哈 看步驟2 這裏如今是inner1咯、
@wrapper2 等價於 inner1 = wrapper2(inner1) = inner2(返回值)
4. 此時的 f = inner2 執行f() 就從inner2()開始執行
5. 執行inner2 首先打印'wrapper2 ,before func'
6. 而後執行func2,由步驟3可知當前wrapper2中的參數是inner1 也就是執行inner1 因此打印wrapper1 ,before func
7. 而後執行func1 參考步驟2 可知這裏的func1()執行的是f()也就是真正的原函數,打印in f
8. 順序執行,打印wrapper1 ,after func
9. inner1執行完(其實就是func2執行完)仍是順序執行 打印wrapper2 ,after func
10. 結果出來了、哈哈
這部分本人能力也只能寫成這樣了,各位看官看不懂那必定是小弟沒表述清楚,不過不要緊,在下再支一招,看圖
結合結果分析,相信各位老闆也能推導出正確的結果了,嗯、真帥!
好,到此對Python裝飾器應該有那麼一丟丟的認識了哈,我無論,就得有認識。下面的內容和文章關係不大哈