當前位置:
首頁 > 知識 > 看完這篇文章還會不懂Python裝飾器?掐死小編吧

看完這篇文章還會不懂Python裝飾器?掐死小編吧

點擊上方

Python開發

」,選擇「置頂公眾號」



關鍵時刻,第一時間送達!



1、必備



#### 第一波 ####


def

foo

()

:


   print(

"foo"

)
foo    

#表示是函數


foo()  

#表示執行foo函數

#### 第二波 ####


def

foo

()

:


   print(

"foo"

)
foo =

lambda

x: x +

1


foo(

1

)  

# 執行下面的lambda表達式,而不再是原來的foo函數,因為函數 foo 被重新定義了


2.需求來了


初創公司有N個業務部門,1個基礎平台部門,基礎平台負責提供底層的功能,如:資料庫操作redis調用監控API等功能。業務部門使用基礎功能時,只需調用基礎平台提供的功能即可。如下:

############### 基礎平台提供的功能如下 ###############


def

f1

()

:

   print(

"f1"

)

def

f2

()

:


   print(

"f2"

)

def

f3

()

:

   print(

"f3"

)

def

f4

()

:


   print(

"f4"

)

############### 業務部門A 調用基礎平台提供的功能 ###############


f1()
f2()
f3()
f4()

############### 業務部門B 調用基礎平台提供的功能 ###############


f1()
f2()
f3()
f4()

目前公司有條不紊的進行著,但是,以前基礎平台的開發人員在寫代碼時候沒有關注驗證相關的問題,即:基礎平台的提供的功能可以被任何人使用。現在需要對基礎平台的所有功能進行重構,為平台提供的所有功能添加驗證機制,

即:執行功能前,先進行驗證。



  • 老大把工作交給 Low B,他是這麼做的:


    跟每個業務部門交涉,每個業務部門自己寫代碼,調用基礎平台的功能之前先驗證。誒,這樣一來基礎平台就不需要做任何修改了。
    當天Low B 被開除了…



  • 老大把工作交給 Low BB,他是這麼做的:


    只對基礎平台的代碼進行重構,讓N業務部門無需做任何修改
    過了一周 Low BB 被開除了…

############### 基礎平台提供的功能如下 ###############


def

f1

()

:


   

# 驗證1

   

# 驗證2


   

# 驗證3


   print(

"f1"

)

def

f2

()

:


   

# 驗證1


   

# 驗證2


   

# 驗證3

   print(

"f2"

)

def

f3

()

:


   

# 驗證1


   

# 驗證2


   

# 驗證3


   print(

"f3"

)

def

f4

()

:


   

# 驗證1


   

# 驗證2


   

# 驗證3


   print(

"f4"

)

############### 業務部門不變 ###############


### 業務部門A 調用基礎平台提供的功能###


f1()
f2()
f3()
f4()

### 業務部門B 調用基礎平台提供的功能 ###


f1()
f2()
f3()
f4()





  • 老大把工作交給 Low BBB,他是這麼做的:


    只對基礎平台的代碼進行重構,其他業務部門無需做任何修改。



############### 基礎平台提供的功能如下 ###############


def

check_login

()

:


   

# 驗證1


   

# 驗證2


   

# 驗證3


   

pass


def

f1

()

:


   check_login()
   print(

"f1"

)

def

f2

()

:


   check_login()
   print(

"f2"

)

def

f3

()

:


   check_login()
   print(

"f3"


def

f4

()

:


   check_login()
   

print

"f4"



老大看了下Low BBB 的實現,嘴角漏出了一絲的欣慰的笑,語重心長的跟Low BBB聊了個天:


老大說:


寫代碼要遵循開發封閉原則,雖然這個原則是用的面向對象開發,但是也適用於函數式編程,簡單來說,它規定已經實現的功能代碼不允許被修改,但可以被擴展,即:




  • 封閉:已實現的功能代碼塊



  • 開放:對擴展開發


如果將開放封閉原則應用在上述需求中,那麼就不允許在函數f1 f2 f3 f4

 

的內部進行修改代碼,老闆就給了Low BBB一個實現方案:

def

w1

(func)

:


   

def

inner

()

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func()
   

return

inner

@w1


def

f1

()

:


   print(

"f1"

)

@w1


def

f2

()

:


   print(

"f2"

)

@w1


def

f3

()

:


   print(

"f3"

)

@w1


def

f4

()

:


   print(

"f4"

)

對於上述代碼,也是僅僅對基礎平台的代碼進行修改,就可以實現在其他人調用函數

 

f1 f2 f3 f4

 

之前都進行驗證操作,並且其他業務部門無需做任何操作。


Low BBB心驚膽戰的問了下,這段代碼的內部執行原理是什麼呢?


老大正要生氣,突然Low BBB的手機掉到地上,恰恰屏保就是Low BBB的女友照片,老大一看一緊一抖,喜笑顏開,交定了Low BBB這個朋友。詳細的開始講解了:


單獨以f1為例:

def

w1

(func)

:


   

def

inner

()

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func()
   

return

inner

@w1


def

f1

()

:


   print(

"f1"

)

當寫完這段代碼後(函數未被執行、未被執行、未被執行),python解釋器就會從上到下解釋代碼,步驟如下:




  1. def w1(func):  ==>w1函數載入到內存



  2. @w1


沒錯,

從表面上看

解釋器僅僅會解釋這兩句代碼,因為函數在

沒有被調用之前

其內部代碼不會被執行。


從表面上看解釋器著實會執行這兩句,但是

 

@w1

 

這一句代碼里卻有大文章,@函數名

 

是python的一種語法糖。


如上例@w1內部會執行以下操作:




  • 執行w1函數,並將

     

    @w1

     

    下面的 函數 作為w1函數的參數,即:@w1 等價於 w1(f1)


所以,內部就會去執行:

def

inner

:


   

#驗證


   

return

f1()

# func是參數,此時 func 等於 f1


return

inner

# 返回的 inner,inner代表的是函數,非執行函數



其實就是將原來的 f1 函數塞進另外一個函數中。




  • 將執行完的

     

    w1

     

    函數返回值賦值給@w1下面的函數的函數名


w1函數的返回值是:

 

def

inner

:


       

#驗證


       

return

原來f1()  

# 此處的 f1 表示原來的f1函數



然後,將此返回值再重新賦值

 

f1,即:

新f1 =

def

inner

:


           

#驗證


           

return

原來f1()

所以,以後業務部門想要執行

 

f1

 

函數時,就會執行

 

新f1

 

函數,在

 

新f1

 

函數內部先執行驗證,再執行原來的f1函數,然後將

 

原來f1

 

函數的返回值 返回給了業務調用者。


如此一來, 即執行了驗證的功能,又執行了原來f1函數的內容,並將原f1函數返回值

 

返回給業務調用著。


Low BBB 你明白了嗎?要是沒明白的話,我晚上去你家幫你解決吧!!!


先把上述流程看懂,之後還會繼續更新…


3.問答時間




  • 問題:被裝飾的函數如果有參數呢?


一個參數:

def

w1

(func)

:


   

def

inner

(arg)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(arg)
   

return

inner

@w1


def

f1

(arg)

:


   print(

"f1"

)

兩個參數:

def

w1

(func)

:


   

def

inner

(arg1,arg2)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(arg1,arg2)
   

return

inner

@w1


def

f1

(arg1,arg2)

:


   print(

"f1"

)

三個參數:

def

w1

(func)

:


   

def

inner

(arg1,arg2,arg3)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(arg1,arg2,arg3)
   

return

inner

@w1


def

f1

(arg1,arg2,arg3)

:


   print(

"f1"

)





  • 問題:可以裝飾具有處理n個參數的函數的裝飾器?



def

w1

(func)

:


   

def

inner

(*args,**kwargs)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(*args,**kwargs)
   

return

inner

@w1


def

f1

(arg1,arg2,arg3)

:


   print(

"f1"

)





  • 問題:一個函數可以被多個裝飾器裝飾嗎?



def

w1

(func)

:


   

def

inner

(*args,**kwargs)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(*args,**kwargs)
   

return

inner

def

w2

(func)

:


   

def

inner

(*args,**kwargs)

:


       

# 驗證1


       

# 驗證2


       

# 驗證3


       

return

func(*args,**kwargs)
   

return

inner

@w1


@w2


def

f1

(arg1,arg2,arg3)

:


   print(

"f1"

)





  • 問題:還有什麼更吊的裝飾器嗎?



#!/usr/bin/env python


#coding:utf-8


def

Before

(request,kargs)

:


   print(

"before"

)

def

After

(request,kargs)

:


   print(

"after"

)

def

Filter

(before_func,after_func)

:


   

def

outer

(main_func)

:


       

def

wrapper

(request,kargs)

:


           before_result = before_func(request,kargs)
           

if

(before_result !=

None

):
               

return

before_result;
           main_result = main_func(request,kargs)
           

if

(main_result !=

None

):
               

return

main_result;
           after_result = after_func(request,kargs)
           

if

(after_result !=

None

):
               

return

after_result;
       

return

wrapper
   

return

outer

@Filter(Before, After)


def

Index

(request,kargs)

:


   print(

"index"

)

4.functools.wraps


上述的裝飾器雖然已經完成了其應有的功能,

即:裝飾器內的函數代指了原函數

,注意其只是代指而非相等,原函數的元信息沒有被賦值到裝飾器函數內部。例如:函數的注釋信息。

def

outer

(func)

:


   

def

inner

(*args, **kwargs)

:


       print(inner.__doc__)  

# None


       

return

func()
   

return

inner

@outer


def

function

()

:


   

"""
   asdfasd
   :return:
   """


   print(

"func"

)

如果使用@functools.wraps裝飾裝飾器內的函數,那麼就會代指元信息和函數。

def

outer

(func)

:


   @functools.wraps(func)


   

def

inner

(*args, **kwargs)

:


       print(inner.__doc__)  

# None


       

return

func()
   

return

inner

@outer


def

function

()

:


   

"""
   asdfasd
   :return:
   """


   print(

"func"

)



至此,關於Python裝飾器的介紹就完畢了,你get到了嗎?







  • 源:

    武沛齊  



  • 原文鏈接:http://www.cnblogs.com/wupeiqi/  



  • Python開發整理髮布,轉載請聯繫作者獲得授權


【點擊成為Android大神】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

如果編程語言是飛機
使用Python自動提取內容摘要

TAG:Python開發 |