定義:
本質(zhì)上是一個(gè)函數(shù)自脯。作用是用來(lái)裝飾另一個(gè)函數(shù)(即被裝飾函數(shù))剩檀,給被裝飾函數(shù)添加功能澄干。前提是不能改變被裝飾函數(shù)的源代碼和調(diào)用方式慧库。這樣的一個(gè)函數(shù)稱(chēng)之為裝飾器跷跪。
解析:
下面我們?cè)挷欢嗾f(shuō),直接用代碼說(shuō)明齐板。下面是一個(gè)函數(shù)吵瞻。
1 def add():
2 b=1+2
3 print(b)
4
5 add()
程序輸出:
————————
3
————————
- 現(xiàn)在我要給這個(gè)函數(shù)增加一個(gè)解釋性的句子,如下甘磨,我們可以編寫(xiě)一個(gè)裝飾器:
#原函數(shù)
def add():
a=1+2
print(a)
#裝飾器
def decorator(func):
def warpper():
print("1+2的結(jié)果是:")
func()
return warpper
#注意此句
add=decorator(add)
#調(diào)用函數(shù)
add()
程序輸出:
——————————
1+2的結(jié)果是:
3
——————————
- 這樣我們就成功的達(dá)成了我們的目的橡羞。這里要注意第12行的這一句,這一句是將add這個(gè)函數(shù)對(duì)象傳入了decorator()函數(shù)济舆,返回的是一個(gè)新函數(shù)變量卿泽,這個(gè)新函數(shù)對(duì)象又重新賦值給add,這樣就可以保證不改變被裝飾函數(shù)的調(diào)用方式不變滋觉。在Python語(yǔ)法中有一種更優(yōu)雅的方式可以代替第十二行的語(yǔ)句签夭。如下:
#裝飾器
def decorator(func):
def warpper():
print("1+2的結(jié)果是:")
func()
return warpper
#add=decorator(add)
#原函數(shù)
@decorator#換成@符號(hào)
def add():
a=1+2
print(a)
#調(diào)用函數(shù)
add()
在被裝飾函數(shù)前面直接加上“@xxx”(xxx為裝飾器函數(shù)名)即可
被裝飾函數(shù)有參數(shù)怎么辦?
- 如果被裝飾器函數(shù)有參數(shù)呢椎侠?該怎們班第租?不用擔(dān)心,我們可以用不定參數(shù)的形式來(lái)收集參數(shù)我纪。實(shí)例代碼如下:
def decorator(func):
def warpper(*args,**kwargs):
print("相加的結(jié)果是:")
func(*args,**kwargs)
return warpper
@decorator
def add(x,y):
a=x+y
print(a)
add(2,3)
程序輸出:
——————————————————
相加的結(jié)果是:
5
——————————————————
如上慎宾,我們給包裝函數(shù)加上接收參數(shù),然后傳給func()函數(shù)就行了宣羊。這樣不管被裝飾函數(shù)有怎樣的參數(shù)都不怕了璧诵。
下面寫(xiě)一個(gè)頁(yè)面驗(yàn)證的裝飾器。
- 大家知道有些網(wǎng)站的一部分頁(yè)面是要求用戶登錄之后才可以訪問(wèn)的仇冯,比如下面的三個(gè)函數(shù)(分別代表三個(gè)頁(yè)面):
def index():
print("welcome to the index page")
def home():
print("welcome to the home page")
def bbs():
print("welcome to the bbs page")
return "I am the return contents"
- 假如說(shuō)現(xiàn)在我們要給home頁(yè)面和bbs頁(yè)面加上驗(yàn)證之宿,顯然現(xiàn)在更改源代碼是不可行的。這個(gè)時(shí)候我們可以用裝飾器苛坚,如下:
username,passwd="jack","abc123"#模擬一個(gè)已登錄用戶
def decorator(func):
def warpper(*args,**kwargs):
Username=input("Username:").strip()
password=input("Password:").strip()
if username==Username and passwd==password:
print("Authenticate Success!")
func(*args,**kwargs)
else:
exit("Username or password is invalid比被!")
return warpper
def index():
print("welcome to the index page")
@decorator
def home():
print("welcome to the home page")
@decorator
def bbs():
print("welcome to the bbs page")
return "I am the return contents"
index()
home()
bbs()
程序結(jié)果:
————————
welcome to the index page #index頁(yè)面未驗(yàn)證直接可以登入
Username:jack
Password:abc123
Authenticate Success! #登錄的而情形
welcome to the home page
Username:jack #密碼或用戶名錯(cuò)誤的情形
Password:123
Username or password is invalid色难!
————————
- 我們注意到bbs()是有返回值的,如果我們把上述代碼的最后一句(第25行)改為“print(bbs())”之后再看看他的輸出結(jié)果:
————————
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()的返回值打印出來(lái)竟然是None笤妙。怎么會(huì)這樣?這樣的話不就改變了被裝飾函數(shù)的源代碼了嗎噪裕?怎樣才能解決呢蹲盘?
我們來(lái)分析一下:
我們執(zhí)行bbs函數(shù)其實(shí)就相當(dāng)于執(zhí)行了裝飾器里的wrapper函數(shù),仔細(xì)分析裝飾器發(fā)現(xiàn)wrapper函數(shù)卻沒(méi)有返回值膳音,所以為了讓他可以正確保證被裝飾函數(shù)的返回值可以正確返回召衔,那么需要對(duì)裝飾器進(jìn)行修改:
username,passwd="jack","abc123"#模擬一個(gè)已登錄用戶
def decorator(func):
def warpper(*args,**kwargs):
Username=input("Username:").strip()
password=input("Password:").strip()
if username==Username and passwd==password:
print("Authenticate Success!")
return func(*args,**kwargs)#在這里加一個(gè)return就行了
else:
exit("Username or password is invalid!")
return warpper
def index():
print("welcome to the index page")
@decorator
def home():
print("welcome to the home page")
@decorator
def bbs():
print("welcome to the bbs page")
return "I am the return contents"
index()
home()
bbs()
- 如圖加上return就可以解決了祭陷。下面我們?cè)诳纯锤暮蟮某绦蜉敵觯?/li>
————————
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()的返回值得到了正確的返回
——-——————
好了苍凛,返回值的問(wèn)題解決了.
既然裝飾器是一個(gè)函數(shù),那裝飾器可以有參數(shù)嗎兵志?
- 答案是肯定的醇蝴。我們同樣可以給裝飾器加上參數(shù)。比如還是上面的三個(gè)頁(yè)面函數(shù)作為例子毒姨,我們可以根據(jù)不同頁(yè)面的驗(yàn)證方式來(lái)給程序不同的驗(yàn)證哑蔫,而這個(gè)驗(yàn)證方式可以以裝飾器的參數(shù)傳入,這樣我們就得在裝飾器上在嵌套一層函數(shù) 了:
username,passwd="jack","abc123"#模擬一個(gè)已登錄用戶
def decorator(auth_type):
def out_warpper(func):
def warpper(*args,**kwargs):
Username=input("Username:").strip()
password=input("Password:").strip()
if auth_type=="local":
if username==Username and passwd==password:
print("Authenticate Success!")
return func(*args,**kwargs)
else:
exit("Username or password is invalid弧呐!")
elif auth_type=="unlocal":
print("HERE IS UNLOCAL AUTHENTICATE WAYS")
return warpper
return out_warpper
def index():
print("welcome to the index page")
@decorator(auth_type="local")
def home():
print("welcome to the home page")
@decorator(auth_type="unlocal")
def bbs():
print("welcome to the bbs page")
return "I am the return contents"
index()
home()
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
————————
可見(jiàn)闸迷,程序分別加入了第2行和第16行和中間的根據(jù)auth_type參數(shù)的判斷的相關(guān)內(nèi)容后, 就解決上述問(wèn)題了俘枫。對(duì)于上面的這一個(gè)三層嵌套的相關(guān)邏輯腥沽,大家可以在 pycharm里頭加上斷點(diǎn),逐步調(diào)試鸠蚪,便可發(fā)現(xiàn)其中的道理今阳。
-
總結(jié)
要想學(xué)好迭代器就必須理解一下三條:
1.函數(shù)即變量(即函數(shù)對(duì)象的概念)
2.函數(shù)嵌套
3.函數(shù)式編程