這是看廖老師python教程的第一個(gè)的筆記,因?yàn)檫@是這份教程最難的章節(jié)之一,我來來回回看了三遍,終于有所突破,寫在這里是為了鞏固自己的理解,同時(shí)也是希望有錯(cuò)的地方能夠得到指正。具體內(nèi)容見廖雪峰老師的課程原址。
裝飾器,起到增強(qiáng)函數(shù)的作用,同時(shí)我們不需要改動(dòng)這個(gè)函數(shù)。這個(gè)部分之所以難,我覺得主要是難在對于返回函數(shù)的多次調(diào)用,容易打亂初學(xué)者的思路。接下來將分為以下三部分進(jìn)行介紹:
初識(shí)裝飾器
首先,裝飾器要修飾的是一個(gè)函數(shù),因此接收到函數(shù)是主體,是裝飾器首要考慮的參數(shù)。以下是一個(gè)裝飾器的基本模板。
#這是一個(gè)裝飾器,接收一個(gè)函數(shù)func作為參數(shù)def decorator(func): #接下來,要建一個(gè)對函數(shù)func進(jìn)行處理的函數(shù)wrapper(這是裝飾器的意義所在嘛) #這個(gè)函數(shù)是有要求的:1.接收所要處理函數(shù)func的參數(shù)(以wrapper參數(shù)接收)。2.函數(shù)func的返 #回值,需要通過wrapper的返回值傳回 def wrapper(*args, **kw): {一系列函數(shù)調(diào)用前的操作} r = func(*args, **kw) #r保存了返回值 {一系列函數(shù)調(diào)用后的操作} return r return wrapper
爾后,這里有一個(gè)返回函數(shù),返回函數(shù),在調(diào)用外圍函數(shù)的時(shí)候,它是不會(huì)馬上執(zhí)行的,需要接受到它特有的參數(shù)才行。
我們分析下課本的例子體會(huì)一下。首先,是一個(gè)簡單的函數(shù)。
def now(): print("2015-3-25")
現(xiàn)在,我們希望在這個(gè)基礎(chǔ)上,打印出函數(shù)的調(diào)用日志。我們來填充上面那個(gè)模板。
def decorator(func): def wrapper(*args, **kw): #填充,函數(shù)執(zhí)行前日志 print('%s() start'%func.__name__) r = func(*args, **kw) #r保存了返回值 #填充,函數(shù)執(zhí)行后日志 print('%s() end'%func.__name__) return r return wrapper
接下來調(diào)用這個(gè)函數(shù)
now = decorator(now) #這里是一個(gè)返回函數(shù),返回了wrappernow()
返回值如下
接下來我們希望簡化上面的過程,只需在now()定義的上方加上@decorator,看起來高大上,其實(shí)就是想說,now = decorator(now)。有了這個(gè)符號(hào)以后,直接調(diào)用now(),效果和上方完全一樣了有木有,可以動(dòng)手試一試。
@decoratordef now(): print("2015-3-25")
關(guān)于參數(shù)傳遞和返回值處理
由于上一個(gè)例子的now()沒有參數(shù),我們實(shí)際上并不能很好的體會(huì)裝飾器內(nèi)部的參數(shù)傳遞機(jī)制,也就是def wrapper(*args, **kw)這里的參數(shù)有什么用。接下來看下一個(gè)例子。
錯(cuò)誤提示里說,wrapper不能接受參數(shù)而add函數(shù)給了兩個(gè)參數(shù),啥意思?這里明明是在給add傳參那。如果看到這里犯了這樣的迷糊,那對裝飾器的原理還沒有理解。
上一節(jié)說過,當(dāng)加了@decorator之后,就相當(dāng)于add = decorator(add)。所以,這個(gè)時(shí)候add已經(jīng)不是add了,它其實(shí)是返回的wrapper;所以,我們在In[3]的地方看到的add函數(shù),實(shí)際上是wrapper函數(shù)了。
<u style="box-sizing: border-box; outline: 0px; overflow-wrap: break-word;">到此,可以看得很清楚,add需要的參數(shù),需要通過wrapper的參數(shù)來傳遞,它的返回值,則需要通過wrapper的返回值來返回。</u>(注:此時(shí)原add函數(shù)通過decorator(func)函數(shù)的形參func保存在外圍函數(shù)中,具體原理且見返回函數(shù)那一節(jié)),做了修正,我們得到如下的運(yùn)行結(jié)果。
到這里已經(jīng)很完美了,但是,我們希望裝飾器是一個(gè)通用物件,它不止是為add函數(shù)服務(wù)呀,還可以為其他各種函數(shù)打印運(yùn)行日志。這個(gè)時(shí)候,便引入可變參數(shù)來替代原來的位置參數(shù)。運(yùn)行如下。
補(bǔ)充關(guān)于裝飾器有參數(shù)的說明
到這里其實(shí)裝飾器的基本原理已經(jīng)說明白了,但是這一節(jié)還有一個(gè)知識(shí)點(diǎn)有點(diǎn)捉急,如果裝飾器本身也有參數(shù)(比如下文的text),就不止要操作的函數(shù)func有參數(shù)了。這里直接用課本的例子如下:
這里要指出,其實(shí)裝飾器本質(zhì)上就是一個(gè)處理函數(shù)的函數(shù),因此核心是接收了func的那個(gè)函數(shù)(在本文就是decorator,因?yàn)樗邮詹⑻幚砗瘮?shù)),至于外圍的log只是希望借助返回參數(shù)的閉包原理鎖住text參數(shù)的。這一點(diǎn)由In[11]上的@log('execute')就可以看的很明白。log('exwcute')是啥,其實(shí)就是處理func的decorator函數(shù)呢。
技巧:不管嵌套了幾層返回函數(shù),最后@符號(hào)后面跟住的一定是處理函數(shù)的那個(gè)函數(shù),如本文@decorator中的decorator函數(shù),還有@log('execute'),其中l(wèi)og('execute')返回的返回函數(shù),不正是decorator嗎。