1. 程式人生 > >python--裝飾器詳解

python--裝飾器詳解

blog 內容 class align fun turn strip 叠代器 ros

Python---裝飾器詳解

定義:

本質上是一個函數。作用是用來裝飾另一個函數(即被裝飾函數),給被裝飾函數添加功能。前提是不能改變被裝飾函數的源代碼和調用方式。這樣的一個函數稱之為裝飾器。

解析:

下面我們話不多說,直接用代碼說明。下面是一個函數。

1 def add():
2     b=1+2
3     print(b
4
5 add()

程序輸出:

————————

3

————————

現在我要給這個函數增加一個解釋性的句子,如下,我們可以編寫一個裝飾器:

 1 #原函數
 2 def add():
 3     a=1+2
 4     print(a)  
 5 #
裝飾器 6 def decorator(func): 7 def warpper(): 8 print("1+2的結果是:") 9 func() 10 return warpper 11 #註意此句 12 add=decorator(add) 13 #調用函數 14 add()

程序輸出:

——————————

1+2的結果是:

3

——————————

這樣我們就成功的達成了我們的目的。這裏要註意第12行的這一句,這一句是將add這個函數對象傳入了decorator()函數,返回的是一個新函數變量,這個新函數對象又重新賦值給add,這樣就可以保證不改變被裝飾函數的調用方式不變。在Python語法中有一種更優雅的方式可以代替第十二行的語句。如下:

 1  #裝飾器
 2 def decorator(func):
 3     def warpper():
 4         print("1+2的結果是:")
 5         func()
 6     return warpper
 7 
 8 #add=decorator(add)
 9 #原函數
10 @decorator#[email protected]
11 def add():
12     a=1+2
13     print(a)
14 #調用函數
15 add()

[email protected](xxx為裝飾器函數名)即可

被裝飾函數有參數怎麽辦

如果被裝飾器函數有參數呢?該怎們班?不用擔心,我們可以用不定參數的形式來收集參數。實例代碼如下:

 1 def decorator(func):
 2     def warpper(*args,**kwargs):
 3         print("相加的結果是:")
 4         func(*args,**kwargs)
 5     return warpper
 6 
 7 @decorator
 8 def add(x,y):
 9     a=x+y
10     print(a)
11 
12 add(2,3)
程序輸出:
——————————————————
相加的結果是:
5
——————————————————

如上,我們給包裝函數加上接收參數,然後傳給func()函數就行了。這樣不管被裝飾函數有怎樣的參數都不怕了。

下面寫一個頁面驗證的裝飾器。

大家知道有些網站的一部分頁面是要求用戶登錄之後才可以訪問的,比如下面的三個函數(分別代表三個頁面):

1 def index():
2     print("welcome to the index page")
3 def home():
4     print("welcome to the home page")
5 def bbs():
6     print("welcome to the bbs page")
7     return "I am the return contents"

假如說現在我們要給home頁面和bbs頁面加上驗證,顯然現在更改源代碼是不可行的。這個時候我們可以用裝飾器,如下:

 1 username,passwd="jack","abc123"#模擬一個已登錄用戶
 2 def decorator(func):
 3     def warpper(*args,**kwargs):
 4         Username=input("Username:").strip()
 5         password=input("Password:").strip()
 6         if username==Username and passwd==password:
 7             print("Authenticate Success!")
 8             func(*args,**kwargs)
 9         else:
10             exit("Username or password is invalid!")
11     return warpper
12 
13 def index():
14     print("welcome to the index page")
15 @decorator
16 def home():
17     print("welcome to the home page")
18 @decorator
19 def bbs():
20     print("welcome to the bbs page")
21     return "I am the return contents"
22 
23 index()
24 home()
25 bbs()

程序結果:

————————

welcome to the index page #index頁面未驗證直接可以登入
Username:jack
Password:abc123
Authenticate Success! #登錄的而情形
welcome to the home page
Username:jack #密碼或用戶名錯誤的情形
Password:123
Username or password is invalid!
————————

我們註意到bbs()是有返回值的,如果我們把上述代碼的最後一句(第25行)改為“print(bbs())”之後再看看他的輸出結果:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
None #返回值能麽成None了???

————————

What happened! bbs()的返回值打印出來竟然是None。怎麽會這樣?這樣的話不就改變了被裝飾函數的源代碼了嗎?怎樣才能解決呢?

我們來分析一下:

我們執行bbs函數其實就相當於執行了裝飾器裏的wrapper函數,仔細分析裝飾器發現wrapper函數卻沒有返回值,所以為了讓他可以正確保證被裝飾函數的返回值可以正確返回,那麽需要對裝飾器進行修改:

1 username,passwd="jack","abc123"#模擬一個已登錄用戶
 2 def decorator(func):
 3     def warpper(*args,**kwargs):
 4         Username=input("Username:").strip()
 5         password=input("Password:").strip()
 6         if username==Username and passwd==password:
 7             print("Authenticate Success!")
 8            return func(*args,**kwargs)#在這裏加一個return就行了
 9         else:
10             exit("Username or password is invalid!")
11     return warpper
12 
13 def index():
14     print("welcome to the index page")
15 @decorator
16 def home():
17     print("welcome to the home page")
18 @decorator
19 def bbs():
20     print("welcome to the bbs page")
21     return "I am the return contents"
22 
23 index()
24 home()
25 bbs()

如圖加上第8行的return就可以解決了。下面我們在看看改後的程序輸出:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
I am the return contents #bbs()的返回值得到了正確的返回

——-——————

好了,返回值的問題解決了.

既然裝飾器是一個函數,那裝飾器可以有參數嗎?

答案是肯定的。我們同樣可以給裝飾器加上參數。比如還是上面的三個頁面函數作為例子,我們可以根據不同頁面的驗證方式來給程序不同的驗證,而這個驗證方式可以以裝飾器的參數傳入,這樣我們就得在裝飾器上在嵌套一層函數 了:

 1 username,passwd="jack","abc123"#模擬一個已登錄用戶
 2 def decorator(auth_type):
 3     def out_warpper(func):
 4         def warpper(*args,**kwargs):
 5             Username=input("Username:").strip()
 6             password=input("Password:").strip()
 7             if auth_type=="local":
 8                 if username==Username and passwd==password:
 9                     print("Authenticate Success!")
10                     return func(*args,**kwargs)
11                 else:
12                     exit("Username or password is invalid!")
13             elif auth_type=="unlocal":
14                 print("HERE IS UNLOCAL AUTHENTICATE WAYS")
15         return warpper
16     return out_warpper
17 
18 def index():
19     print("welcome to the index page")
20 @decorator(auth_type="local")
21 def home():
22     print("welcome to the home page")
23 @decorator(auth_type="unlocal")
24 def bbs():
25     print("welcome to the bbs page")
26     return "I am the return contents"
27 
28 index()
29 home()
30 bbs()

輸出:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
HERE IS UNLOCAL AUTHENTICATE WAYS

————————

可見,程序分別加入了第2行和第16行和中間的根據auth_type參數的判斷的相關內容後, 就解決上述問題了。對於上面的這一個三層嵌套的相關邏輯,大家可以在 pycharm裏頭加上斷點,逐步調試,便可發現其中的道理。

總結

要想學好叠代器就必須理解一下三條:

1.函數即變量(即函數對象的概念)

2.函數嵌套

3.函數式編程

作者:彭前超(QQ:3480487308)

時間:2017/6/10 23:09

轉載請註明出處,謝謝合作。本人另有Python視頻教程,想要的qq聯系,非誠勿擾.

python--裝飾器詳解