Python 裝飾器

裝飾器

什麼是裝飾器

裝飾器顧名思義就是一個有裝飾功能的工具,那麼裝飾器又是用來裝飾什麼的?為什麼要裝飾這個東西?裝飾的目的是什麼呢?本文會一一作答讓小弟一個一個說

開放封閉原則

談及裝飾器就要引申一個概念,那就是開放封閉原則,那麼問題又來了 什麼是開放封閉……好好好,直接說這個,開放封閉本來是兩個對立的概念,也就是說是一對反義詞,那麼為什麼要提出開放封閉原則呢?原因是在日常的開發工作中,一般最初上線的產品的功能是不盡完善的,就是說不夠完美但已經能夠支撐日常使用,其餘的功能可以日後擴展,舉例:N年前的QQ和現在的QQ(PS:雖然現在本人不怎麼用了)。在後期擴展功能時,因為函數已經是寫好的,而且存在大量調用,所以要直接去給函數增添新的功能顯然不現實,所以我們只能新建函數去給原來的函數擴展功能(其實這就是一個裝飾器啦)

那麼開放封閉原則到底是什麼? 答案是對源碼封閉,對新功能開放

  • 封閉原則:不要改變源代碼

  • 開放原則:能增加一些額外的功能

    如果還有客觀對開放封閉原則似知似解的話,沒關係接着往下看,不影響您食用本文,因為Python裝飾器本身就是對開放封閉原則完美的詮釋

裝飾器初識

不低調的說,裝飾器就是一個函數,名字本來很高大上,但本質就是一個函數,裝飾器函數的功能就是要裝飾一個函數,在不改變被裝飾函數的源代碼及調用方式的前提下,為其增加額外的功能。是不是有點門道了,是不是覺得這玩意也沒啥啦,真是優秀的同學。

代碼show(技術博客不寫代碼干白話說出去丟人)

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作為內層函數的返回值返回給函數執行者。

語法糖

@warpper這東西和 func = wrapper(func)是一樣的,也就是說最後三行代碼可以這樣寫

#@warpper 
def func():
  print('我就是那個被裝飾的函數')
func = wrapper(func)   #注意奧,這玩意要寫在被裝飾函數的下邊,語法糖才寫上邊

至於為什麼要寫着東西,或者為什麼要用語法糖?請聽下回分解~~~,收起你滴拳頭,是這樣,我剛開始的時候談到了,裝飾器是要在不改變源碼和其調用方式的前提下給其增加新的功能,注意到了么 調用方式 嗯……沒錯就是調用方式,如果我不這樣寫那我是不是要wrapper(func)()這樣去調用啊,是不是有點繞了,但是我把wrapper(func)賦值給了一個和被裝飾函數同名的變量,那我此時要怎麼調用,是不是就是func(),這樣就滿足了開放封閉原則,完美!其實本質就是要把裝飾器“偽裝”成原函數,包括調用方式、參數、返回值,裝就要裝的像一點,對吧。

裝飾帶參數的函數
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
@wrapper
def func():
return('我不管,我最帥')

哈哈,這段代碼中有我的心聲,你們都懂的,很簡單,裝飾有返回值的參數,在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')
def wangzhi1():
   print('歡迎訪問網址1')
   
@wrapper_out('webpage2')
def wangzhi1():
   print('歡迎訪問網址2')

如果使用標準的裝飾器函數的話只能裝飾其中的一個函數,當裝飾另一個函數時會因為訪問不到正確的數據庫而報錯。

@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裝飾器應該有那麼一丟丟的認識了哈,我不管,就得有認識。下面的內容和文章關係不大哈

第一篇正經寫的文章,可能文章內容表達不盡如人意的正經哈,但初心是好的,就是分享知識,分享心得嘛,嗯,不管怎麼說,我還是很欣慰的對自己,哈哈,能有人從中有收穫就更perfect嘍

【精選推薦文章】

智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

帶您來看台北網站建置台北網頁設計,各種案例分享

廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

您可能也會喜歡…