python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上
Python高級(jí)編程——裝飾器Decorator超詳細(xì)講解(上篇)
送你小心心記得關(guān)注我哦!!
進(jìn)入正文
全文摘要
裝飾器decorator,是python語(yǔ)言的重要特性,我們平時(shí)都會(huì)遇到,無(wú)論是面向?qū)ο蟮脑O(shè)計(jì)或者是使用相關(guān)的庫(kù),但是很少有文章對(duì)裝飾器的來(lái)龍去脈進(jìn)行深入詳解,導(dǎo)致的結(jié)果就是很多人知其然,不知其所以然,所以我打算出一期關(guān)于Python裝飾器的詳解文章,由于內(nèi)容較多,該文章共分為上下兩篇,本文講解上篇,下篇會(huì)在后面發(fā)出,請(qǐng)記得持續(xù)關(guān)注哦!文章偏長(zhǎng),閱讀全文約20min。
全文目錄
01 python裝飾器誕生的背景
1.1 從一種簡(jiǎn)單的情況說(shuō)起
1.2 非“裝飾器”實(shí)現(xiàn)添加額外功能
02 什么是裝飾器decorator
2.1 裝飾器的兩個(gè)層面的定義
2.2 裝飾起的作用——兩個(gè)方面
2.3 裝飾器的使用場(chǎng)景
03 裝飾器的實(shí)現(xiàn)
3.1 裝飾器的逐步實(shí)現(xiàn)
3.2 裝飾器的一般“模板”
04 裝飾器的分類實(shí)現(xiàn)(下篇預(yù)告)
python裝飾器詳解(上篇)
01
python裝飾器誕生的背景
01 decorator誕生的背景
1.1 從一種簡(jiǎn)單的情況說(shuō)起
裝飾器的定義很是抽象,各個(gè)不同的文章有不同的表述,但是我不打算一開始就告訴你什么是Python的裝飾器,這樣顯太過(guò)突兀,我們來(lái)看一個(gè)小例子。
先定義一個(gè)簡(jiǎn)單的函數(shù):
def myfunc:
print('我是函數(shù)myfunc')
myfunc() #調(diào)用函數(shù)
然后呢,我想看看這個(gè)函數(shù)執(zhí)行這個(gè)函數(shù)用了多長(zhǎng)時(shí)間,好吧,那么我們可以這樣做:
import time
def myfunc:
start = time.clock()
print('我是函數(shù)myfunc')
end = time.clock()
print(f'函數(shù)所花費(fèi)的時(shí)間為 :{end - start}')
myfunc() #函數(shù)調(diào)用
我們現(xiàn)在已經(jīng)達(dá)到了我們的目的。但是如果是我們還想繼續(xù)給另外的一些函數(shù)也實(shí)現(xiàn)同樣的功能。那我們是不是給每個(gè)函數(shù)都添加這么幾句話呢?當(dāng)然可以,但是不高效,而且很麻煩。如果有某一種方式可以一次性解決所有的問(wèn)題,那自然最好不過(guò)了,于是“裝飾器”就應(yīng)運(yùn)而生。
在上面的例子中,函數(shù)本身的功能只是打印一句話而已,但是經(jīng)過(guò)改造后的函數(shù)不僅要能夠打印這一句話,還要能夠顯示函數(shù)執(zhí)行所花費(fèi)的時(shí)間,這相當(dāng)于我要給這個(gè)函數(shù)添加額外的功能,注意這個(gè)關(guān)鍵字,其實(shí)“裝飾器”就是專門給函數(shù)添加額外的功能的。
01 decorator誕生的背景
1.2 非“裝飾器”實(shí)現(xiàn)添加額外功能
還記得嗎,函數(shù)在Python中是一等公民,那么我們可以考慮重新定義一個(gè)函數(shù)timeit,將myfunc的引用傳遞給他,然后在timeit中調(diào)用myfunc并進(jìn)行計(jì)時(shí),這樣,我們就達(dá)到了不改動(dòng)myfunc定義但是又添加了額外功能的目的,代碼如下:
import time
def myfunc():
print("我是函數(shù)myfunc")
def timeit(function):
start = time.clock()
function()
end =time.clock()
print(f'函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:{end-start}')
timeit(myfunc)
運(yùn)行結(jié)果為:
我是函數(shù)myfunc
函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:0.0004924657368762765
上面的代碼看起來(lái)邏輯上并沒(méi)有問(wèn)題,也達(dá)到了我們所要實(shí)現(xiàn)的目的!但是,我們雖然沒(méi)有修改函數(shù)myfunc定義中的代碼,但是我們似乎修改了調(diào)用部分的代碼。原本我們是這樣調(diào)用的:myfunc(),修改以后變成了:timeit(myfunc)。這樣的話,如果myfunc在N處都被調(diào)用了,你就不得不去修改這N處的代碼。或者更極端的,考慮其中某處調(diào)用的代碼無(wú)法修改這個(gè)情況,比如:這個(gè)函數(shù)是你交給別人使用的。
其實(shí)將函數(shù)作為參數(shù)傳遞,已經(jīng)具備了裝飾器的雛形了,但是上面的實(shí)現(xiàn)還不夠好,下面會(huì)給出更好地實(shí)現(xiàn)方式。
02
什么是裝飾器-decorator
02 什么是裝飾器decorator
2.1 裝飾器的定義——兩個(gè)層面
一般而言,如果我需要給函數(shù)添加額外的某一些功能,我需要修改函數(shù)的源代碼,但是如前面所說(shuō),這樣麻煩,而且不高效,裝飾器就是專門的解決方案!
裝飾器的定義主要從兩個(gè)不同層面進(jìn)行說(shuō)明的。
在Python里面有兩層定義:
第一:從設(shè)計(jì)模式的層面上——代碼重用
裝飾器是一個(gè)很著名的設(shè)計(jì)模式,經(jīng)常被用于有切面需求的場(chǎng)景,較為經(jīng)典的應(yīng)用有插入日志、增加計(jì)時(shí)邏輯來(lái)檢測(cè)性能、加入事務(wù)處理等。裝飾器是解決這類問(wèn)題的絕佳設(shè)計(jì),有了裝飾器,我們就可以抽離出大量函數(shù)中與函數(shù)功能本身無(wú)關(guān)的雷同代碼并繼續(xù)重用。概括的講,裝飾器的作用就是為已經(jīng)存在的對(duì)象添加額外的功能。
第二:從Python的語(yǔ)法層面上(其實(shí)第二種本質(zhì)上也是第一種,只不過(guò)在語(yǔ)法上進(jìn)行了規(guī)范化)
簡(jiǎn)言之,python裝飾器就是用于拓展原來(lái)函數(shù)功能的一種函數(shù),這個(gè)函數(shù)的特殊之處在于它的返回值也是一個(gè)函數(shù),使用python裝飾器的好處就是在不用更改原函數(shù)的代碼前提下給函數(shù)增加新的功能。 如此一來(lái),我們要想拓展原來(lái)函數(shù)功能,就不需要再在函數(shù)里面修改源代碼了。
02 什么是裝飾器decorator
2.2 裝飾器的作用——兩個(gè)層面
通過(guò)上面的表述,得出兩個(gè)最基本的結(jié)論,其實(shí)這也是裝飾器兩個(gè)最基本的作用:
(1)抽離雷同代碼,加以重用
(2)為函數(shù)添加額外的功能
02 什么是裝飾器decorator
2.3 裝飾器的使用場(chǎng)景
裝飾器可能在我們平時(shí)的編碼中比較少去自己定義,更多的是我們使用別人的已經(jīng)編寫好的裝飾器,比如我們我們經(jīng)常使用的@staticmethod,@classmethod,@property等等,都是別人寫好了,我們自己不需要自己再實(shí)現(xiàn)了,在編碼中,我們?cè)谙旅娴囊恍┣闆r會(huì)經(jīng)常遇見裝飾器。
(1)緩存裝飾器
(2)權(quán)限驗(yàn)證裝飾器
(3)計(jì)時(shí)裝飾器
(4)日志裝飾器
(5)路由裝飾器
(6)異常處理裝飾器
(7)錯(cuò)誤重試裝飾器
后面我還會(huì)講到關(guān)于python的高級(jí)語(yǔ)法——python描述符(descriptor),其實(shí)也是跟python的裝飾器有著千絲萬(wàn)縷的關(guān)系,詳細(xì)可以參見后面的文章哦!
03
裝飾器decorator的實(shí)現(xiàn)
03 裝飾器decorator的實(shí)現(xiàn)
3.1 裝飾器的逐步實(shí)現(xiàn)
針對(duì)上面改進(jìn)版的代碼所存在的哪些問(wèn)題,我們想出了解決辦法:
既然修改N處的調(diào)用代碼很麻煩,我們就來(lái)想想辦法不修改調(diào)用代碼;如果不修改調(diào)用代碼,也就意味著調(diào)用myfunc()需要產(chǎn)生調(diào)用timeit(myfunc)的效果。
因?yàn)閜ython中一切皆對(duì)象,故而我們可以想到將timeit賦值給myfunc,
import time
def myfunc():
print("我是函數(shù)myfunc")
def timeit(function):
start = time.clock()
function()
end =time.clock()
print(f'函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:{end-start}')
myfunc=timeit #將timeit賦值給原來(lái)的myfunc
myfunc()
但是上面的調(diào)用并不會(huì)成功,會(huì)顯示出如下錯(cuò)誤:
timeit() missing 1 required positional argument: 'function'
為什么呢?這是因?yàn)閷imeit賦值給myfunc之后,此時(shí)myfunc和timeit表示的同一個(gè)東西,但是timeit似乎帶有一個(gè)參數(shù)function,而在調(diào)用myfunc()的時(shí)候并沒(méi)有傳入任何參數(shù),所以并不會(huì)成功。
但是上面的調(diào)用雖然沒(méi)有成功,卻給我們指出了一條重要的線索,因?yàn)樯厦娴拇a已經(jīng)解決“修改調(diào)用代碼”的問(wèn)題,只不過(guò)是參數(shù)沒(méi)有統(tǒng)一而已,那就想辦法把參數(shù)統(tǒng)一吧!那就再添加一個(gè)函數(shù)唄!什么意思?
因?yàn)閰?shù)不統(tǒng)一,如果timeit()不并不是直接添加額外的功能,而是返回一個(gè)與myfunc參數(shù)列表一致的函數(shù)。而原來(lái)timeit需要添加額外功能的代碼再在timeit里面定義一個(gè)函數(shù),由它去完成不就可以了嗎,將timeit(myfunc)的返回值賦值給myfunc,然后,調(diào)用myfunc()的代碼完全不用修改。——即我們依然是調(diào)用myfunc(調(diào)用代碼沒(méi)變),但是同樣卻達(dá)到了添加額外功能的效果!
代碼如下:
import time
#原來(lái)的函數(shù)myfunc
def myfunc():
print("我是函數(shù)myfunc")
#定義一個(gè)計(jì)時(shí)器
def timeit(function):
'''
timeit函數(shù)負(fù)責(zé)返回一個(gè)wrapper,wrapper的參數(shù)要與原來(lái)的myfunc保持相同
這樣一來(lái),執(zhí)行 myfunc=timeit(myfunc) myfunc完全等價(jià)于wrapper
wrapper函數(shù)負(fù)責(zé)添加額外功能
'''
def wrapper():
start = time.clock()
function()
end =time.clock()
print(f'函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:{end-start}')
return wrapper
myfunc=timeit(myfunc) #注意,這里與前面的 “myfunc=timeit”是有所區(qū)別的哦
myfunc() #還和原來(lái)調(diào)用myfunc()一樣,但是達(dá)到了添加額外功能的效果
上面的運(yùn)行結(jié)果就出來(lái)了,如下:
我是函數(shù)myfunc
函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:0.0005973331798019136
總結(jié):在上面的函數(shù)定義和調(diào)用中,看起來(lái)我的調(diào)用myfunc()和原來(lái)并沒(méi)有任何不同,但是卻已經(jīng)添加了額外的效果。它解決前面存在的兩個(gè)問(wèn)題:
(1)不用修改函數(shù)源代碼,也不用修改調(diào)用函數(shù)的代碼,完全跟調(diào)用最原始的myfunc()代碼一樣,但是卻添加了額外功能;
(2)解決了timeit和myfunc的參數(shù)不統(tǒng)一問(wèn)題,那就是再添加一層wrapper;
——這就是裝飾器。
上面的裝飾器就是最原始的版本,但是python中引入了專門的“語(yǔ)法糖”來(lái)實(shí)現(xiàn)裝飾器,這樣看起來(lái)更加專業(yè),更加美觀。就是使用字符“@”去實(shí)現(xiàn)。代碼如下:
import time
#定義一個(gè)計(jì)時(shí)器
def timeit(function):
'''
timeit函數(shù)負(fù)責(zé)返回一個(gè)wrapper,wrapper的參數(shù)要與原來(lái)的myfunc保持相同
這樣一來(lái),執(zhí)行 myfunc=timeit(myfunc) myfunc完全等價(jià)于wrapper
wrapper函數(shù)負(fù)責(zé)添加額外功能
'''
def wrapper():
start = time.clock()
function()
end =time.clock()
print(f'函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:{end-start}')
return wrapper
#myfunc=timeit(myfunc) #注意,這里與前面的 “myfunc=timeit”是有所區(qū)別的哦
#原來(lái)的函數(shù)myfunc
@timeit
def myfunc():
print("我是函數(shù)myfunc")
myfunc() #還和原來(lái)調(diào)用myfunc()一樣,但是達(dá)到了添加額外功能的效果
上面代碼的運(yùn)行結(jié)果依然是:
我是函數(shù)myfunc
函數(shù)執(zhí)行所花費(fèi)的時(shí)間為:0.0004893814003196401
在上面的例子中,在定義myfunc函數(shù)的上面加了一個(gè)@timeit,這與前面的寫法myfunc = timeit(myfunc)完全等價(jià),
@有兩個(gè)重要的作用,
第一:較少了代碼書寫量;
第二:那就是讓我們的代碼看上去更有裝飾器的感覺,看起來(lái)更高端了。
總結(jié)
在這個(gè)例子中,函數(shù)進(jìn)入和退出時(shí)需要計(jì)時(shí),這被稱為一個(gè)橫切面(Aspect),這種編程方式被稱為面向切面的編程(Aspect-Oriented Programming)。與傳統(tǒng)編程習(xí)慣的從上往下執(zhí)行方式相比較而言,像是在函數(shù)執(zhí)行的流程中橫向地插入了一段邏輯。在特定的業(yè)務(wù)領(lǐng)域里,能減少大量重復(fù)代碼。面向切面編程還有相當(dāng)多的術(shù)語(yǔ),這里就不多做介紹,感興趣的話可以去找找相關(guān)的資料(如果有需要,我后面也會(huì)抽時(shí)間專門寫一系列關(guān)于面向切面編程的文章,看我有沒(méi)有時(shí)間啦!)
03 裝飾器decorator的實(shí)現(xiàn)
3.2 裝飾器的一般“模板”
為了能夠明確裝飾器的實(shí)現(xiàn)原理,這里給出一個(gè)關(guān)于裝飾器的“一般模板”,方便大家理解!但是,裝飾器作為一種設(shè)計(jì)模式,本身是沒(méi)有固定的設(shè)計(jì)模板的,語(yǔ)法也是相對(duì)較為靈活,沒(méi)有說(shuō)一定要怎么寫才正確。
模板如下:
def decorator(function):
'''
第一層函數(shù)為裝飾器名稱
function:參數(shù),即需要裝飾的函數(shù)
return:返回值wrapper,為了保持與原函數(shù)參數(shù)一致
'''
def wrapper(*arg,**args):
'''
內(nèi)層函數(shù),這個(gè)函數(shù)實(shí)現(xiàn)“添加額外功能”的任務(wù)
*arg,**args:參數(shù)保持與需要裝飾的函數(shù)參數(shù)一致,這里用*arg和**args代替
'''
#這里就是額外功能代碼
function() #執(zhí)行原函數(shù)
#這里就是額外功能代碼
return wrapper
一般就按照上面這個(gè)模板寫“裝飾器”函數(shù),一般就不會(huì)出錯(cuò)了。
注意事項(xiàng):
(1)裝飾器一般由兩層函數(shù)組成,外層的decorator,和內(nèi)層的wrapper;
(2)第一層函數(shù)的參數(shù)function,即需要裝飾的函數(shù),返回值wrapper,為了保持與原函數(shù)參數(shù)一致
(3)內(nèi)層函數(shù),這個(gè)函數(shù)實(shí)現(xiàn)“添加額外功能”的任務(wù), *arg,**args:參數(shù)保持與需要裝飾的函數(shù)參數(shù)一致,這里用*arg和**args代替。
04
裝飾器的各種花式實(shí)現(xiàn)
04 裝飾器的各種花式實(shí)現(xiàn)
4.1 裝飾器的分類型實(shí)現(xiàn)
學(xué)過(guò)裝飾器的人都知道Python的閉包,關(guān)于“閉包”的詳細(xì)定義有各種版本,但我們經(jīng)常看見這樣一句話,“Python的裝飾器就是一種閉包或者是Python的閉包其實(shí)就是裝飾器”,這句話在一定程度上是不正確的,但是這么說(shuō)也可以(心里要明白二者的本質(zhì))。
本質(zhì):python閉包是裝飾器的真子集,即裝飾器是更加寬泛的概念,至于為什么,它們二者的區(qū)別和聯(lián)系,我會(huì)在下一篇文章里面詳細(xì)說(shuō)明。下一篇參考:
Python高級(jí)編程——裝飾器Decorator詳解(下篇)
不僅如此,上面所實(shí)現(xiàn)的裝飾器是針對(duì)函數(shù)的,實(shí)際上Python的裝飾器可以是“函數(shù)”或者是“類”,而被裝飾的對(duì)象也可以是“函數(shù)”或者是“類”,這樣一來(lái),就有四種搭配情況,即:
函數(shù)裝飾函數(shù)
函數(shù)裝飾類
類裝飾函數(shù)
類裝飾類
具體每一種怎么實(shí)現(xiàn)呢?其實(shí)他們的設(shè)計(jì)思想都是大同小異,只是實(shí)現(xiàn)細(xì)節(jié)略有不同,欲知詳細(xì)情況,且聽下回分解!!!
下一篇預(yù)告:
裝飾器與閉包的聯(lián)系和區(qū)別
四大類裝飾器的搭配實(shí)現(xiàn)
····
本文后面分享三本python使用的電子書,分別是獲取方式:關(guān)注轉(zhuǎn)發(fā)文章私信小編(學(xué)習(xí))就可以了
《python面試寶典》《python核心編程第三版》《利用python進(jìn)行數(shù)據(jù)分析第二版》
獲取方式:關(guān)注轉(zhuǎn)發(fā)文章私信小編(學(xué)習(xí))就可以了
總結(jié)
以上是生活随笔為你收集整理的python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: rust为什么显示不了国服_Rust编程
- 下一篇: 哈利波特魔法觉醒赫敏回响怎么得
