一個(gè)筆試經(jīng)歷畜份,是一道關(guān)于裝飾器的題目玄叠,那時(shí)候的感覺(jué)應(yīng)該是這樣:
- " 美鋁,我們是不是認(rèn)識(shí)? "
俗話說(shuō)得好:一回生二回熟。但學(xué)習(xí)裝飾器時(shí)只是了解下臼闻,同時(shí)久不使用這方面的知識(shí)鸿吆,好刀都要變鈍呀,況且自己拿的還是把破刀述呐。被刺激的不要不要的 ~
首先惩淳,遇到一個(gè)云里霧里的問(wèn)題:
1. 什么是裝飾器?
第一要明確知道的概念:裝飾器屬于Python的高階函數(shù)(聽(tīng)說(shuō)還是最常用到的高階函數(shù))乓搬。兩個(gè)重點(diǎn):
- 它是一個(gè)函數(shù)
- 它是一個(gè)感覺(jué)起來(lái)很高級(jí)的函數(shù)
所以思犁,【劃重點(diǎn)】裝飾器是一個(gè)語(yǔ)法糖,它以一個(gè)函數(shù)為參數(shù)进肯,并返回一個(gè)加強(qiáng)版的原函數(shù)(方法或者類(lèi))對(duì)象激蹲。當(dāng)然,這個(gè)裝飾器要設(shè)計(jì)正確江掩。
有點(diǎn)口語(yǔ)化学辱,那就摘抄一段正式點(diǎn)的概念:
裝飾器本質(zhì)上是一個(gè)Python函數(shù),它可以讓其他函數(shù)在不需要做任何代碼變動(dòng)的前提下增加額外功能环形,裝飾器的返回值也是一個(gè)函數(shù)對(duì)象策泣。它經(jīng)常用于有切面需求的場(chǎng)景,比如:插入日志抬吟、性能測(cè)試萨咕、事務(wù)處理、緩存拗军、權(quán)限校驗(yàn)等場(chǎng)景任洞。裝飾器是解決這類(lèi)問(wèn)題的絕佳設(shè)計(jì),有了裝飾器发侵,我們就可以抽離出大量與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用交掏。
enmm~那裝飾器的作用就是
在不改動(dòng)函數(shù)的情況下,給函數(shù)或者對(duì)象(其實(shí)函數(shù)也是一個(gè)對(duì)象)在運(yùn)行時(shí)動(dòng)態(tài)地額外添加其他的功能刃鳄。
那么就進(jìn)入第二內(nèi)容盅弛,寫(xiě)一點(diǎn)代碼出來(lái)溜溜,來(lái)理解下令人無(wú)趣的概念
2. 入門(mén)級(jí)別的裝飾器(面試題就是入門(mén)級(jí)的叔锐,我好想哭)
別嗶嗶挪鹏,直接上代碼:
# 有個(gè)單純要去看電影的函數(shù),就叫 movie() 吧愉烙,但你又想邊看電影邊吃爆米花讨盒。用裝飾器如何寫(xiě)呢?
def extra(func):
"""給 movie()添加額外的功能"""
def wrapper():
print("我要先把爆米花和可樂(lè)買(mǎi)了步责!")
return func() # 得在加強(qiáng)版原函數(shù)里調(diào)用原函數(shù)返顺。給自己穿漂亮的衣服可不要把自己本身忘記了禀苦。
return wrapper
@extra
def movie():
print("我要看電影。")
movie()
-----------------------------這是運(yùn)行結(jié)果線----------------------------------------
我要先把爆米花和可樂(lè)買(mǎi)了遂鹊!
我要看電影振乏。
extra()
函數(shù)就是一個(gè)裝飾器。(兩點(diǎn)分析)
- 在 extra 函數(shù)中傳入的
func
參數(shù)是一個(gè)函數(shù)秉扑,即movie()
這個(gè)函數(shù)慧邮。符合高階函數(shù)的身份。在以前沒(méi)有加入語(yǔ)法糖時(shí)舟陆,@extra
等價(jià)于movie = extra( movie )
误澳。這里涉及到一個(gè)知識(shí)點(diǎn):
函數(shù)是一個(gè)對(duì)象,函數(shù)對(duì)象還可以賦值給變量吨娜。所以可以通過(guò)變量調(diào)用這個(gè)函數(shù)脓匿。
def cat():
print('This is a ugly cat!')
c = cat # 將函數(shù)對(duì)象賦值給變量c
c()
其實(shí),Python 里所有數(shù)據(jù)——布爾值宦赠、整數(shù)陪毡、浮點(diǎn)數(shù)、字符串,甚至大型數(shù)據(jù)結(jié)構(gòu)勾扭、函數(shù)以及程序——都是以對(duì)象(object)的形式存在的毡琉。
扯遠(yuǎn)了,回歸正題——
-
return wrapper
中的wrapper
就是加強(qiáng)版的movie
函數(shù)妙色。這個(gè)加強(qiáng)版的movie函數(shù)除了有看電影還有了要買(mǎi)爆米花和可樂(lè)的功能桅滋。符合返回一個(gè)加強(qiáng)版的原函數(shù)的概念。
但是——
如果這個(gè)被裝飾器裝飾的函數(shù) moive
需要傳入一個(gè)電影名name
的參數(shù)身辨。那么這個(gè)加強(qiáng)版函數(shù)wrapper
和其里面的函數(shù)func
也要做相應(yīng)的更改丐谋。但 return wrapper
中的wrapper
不需要更改,因裝飾器返回的是一個(gè)對(duì)象煌珊。是不需要添加參數(shù)的号俐。
def extra(func):
"""給 movie()添加額外的功能"""
def wrapper(name): # 一毛一樣的參數(shù)
print("我要先把爆米花和可樂(lè)買(mǎi)了!")
return func(name)
return wrapper
@extra
def movie(name):
print("我要看《{}》電影定庵。".format(name))
movie('蜘蛛俠 英雄歸來(lái)')
----------------迷人的結(jié)果線------------------
我要先把爆米花和可樂(lè)買(mǎi)了吏饿!
我要看《蜘蛛俠 英雄歸來(lái)》電影。
雖然把裝飾器的里的加強(qiáng)版函數(shù)的參數(shù)弄的與原函數(shù)一毛一樣蔬浙,但這裝飾器是一種封裝猪落,如果萬(wàn)一你想看一部,而其他人想看兩部呢畴博?難道又給另個(gè)人寫(xiě)個(gè)相似的裝飾器嗎笨忌?所以讓裝飾器同步更改參數(shù)的做法是不可取的。好辦法就是給裝飾器一個(gè)無(wú)敵的參數(shù)俱病,一個(gè)可變參數(shù)*args
和關(guān)鍵字參數(shù)**kwargs
官疲。
def extra(func):
"""給 movie()添加額外的功能"""
def wrapper(*args, **kwagrs): # 無(wú)敵的參數(shù)了吧
print("我要先把爆米花和可樂(lè)買(mǎi)了杂曲!")
return func(*args, **kwagrs)
return wrapper
@extra
def movie1(name):
print("我要看《{}》電影。".format(name))
@extra
def movie2(name1, name2):
print("我要看兩部電影《{0}》和《{1}》".format(name1, name2))
movie1('蜘蛛俠 英雄歸來(lái)')
print('\n')
movie2('蜘蛛俠 英雄歸來(lái)','猩球崛起')
嘻嘻袁余,好累呀!那么入門(mén)的就結(jié)束了咱揍,進(jìn)入進(jìn)階版本的裝飾器了颖榜。發(fā)車(chē)了~~~吱吱吱~~~
還是下一篇再寫(xiě)吧。