python裝飾器原理與用法深入詳解
本文實(shí)例講述了python裝飾器原理與用法。分享給大家供大家參考,具體如下:
你會(huì)Python嘛?
我會(huì)!
那你給我講下Python裝飾器吧!
Python裝飾器???我沒(méi)用過(guò)哎
以上是我一個(gè)哥們面試時(shí)候發(fā)生的真實(shí)對(duì)白。
----------------------------------------------分割線------------------------------------------------------------------------------
簡(jiǎn)言之,python裝飾器就是用于拓展原來(lái)函數(shù)功能的一種函數(shù),這個(gè)函數(shù)的特殊之處在于它的返回值也是一個(gè)函數(shù),使用python裝飾器的好處就是在不用更改原函數(shù)的代碼前提下給函數(shù)增加新的功能。
一般而言,我們要想拓展原來(lái)函數(shù)代碼,最直接的辦法就是侵入代碼里面修改,例如:
import time def func(): print("hello") time.sleep(1) print("world")
這是我們最原始的的一個(gè)函數(shù),然后我們?cè)噲D記錄下這個(gè)函數(shù)執(zhí)行的總時(shí)間,那最簡(jiǎn)單的做法就是:
#原始侵入,篡改原函數(shù) import time def func(): startTime = time.time() print("hello") time.sleep(1) print("world") endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs)
但是如果你的Boss在公司里面和你說(shuō):“小祁,這段代碼是我們公司的核心代碼,你不能直接去改我們的核心代碼?!蹦窃撛趺崔k呢,我們仿照裝飾器先自己試著寫(xiě)一下:
#避免直接侵入原函數(shù)修改,但是生效需要再次執(zhí)行函數(shù) import time def deco(func): startTime = time.time() func() endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) def func(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f = func deco(f)#只有把func()或者f()作為參數(shù)執(zhí)行,新加入功能才會(huì)生效 print("f.__name__ is",f.__name__)#f的name就是func
這里我們定義了一個(gè)函數(shù)deco,它的參數(shù)是一個(gè)函數(shù),然后給這個(gè)函數(shù)嵌入了計(jì)時(shí)功能。然后你可以拍著胸脯對(duì)老板說(shuō),看吧,不用動(dòng)你原來(lái)的代碼,我照樣拓展了它的函數(shù)功能。
然后你的老板有對(duì)你說(shuō):“小祁,我們公司核心代碼區(qū)域有一千萬(wàn)個(gè)func()函數(shù),從func01()到func1kw(),按你的方案,想要拓展這一千萬(wàn)個(gè)函數(shù)功能,就是要執(zhí)行一千萬(wàn)次deco()函數(shù),這可不行呀,我心疼我的機(jī)器?!?/p>
好了,你終于受夠你老板了,準(zhǔn)備辭職了,然后你無(wú)意間聽(tīng)到了裝飾器這個(gè)神器,突然發(fā)現(xiàn)能滿足你閆博士的要求了。
我們先實(shí)現(xiàn)一個(gè)最簡(jiǎn)陋的裝飾器,不使用任何語(yǔ)法糖和高級(jí)語(yǔ)法,看看裝飾器最原始的面貌:
#既不需要侵入,也不需要函數(shù)重復(fù)執(zhí)行 import time def deco(func): def wrapper(): startTime = time.time() func() endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f = func #這里f被賦值為func,執(zhí)行f()就是執(zhí)行func() f()
這里的deco函數(shù)就是最原始的裝飾器,它的參數(shù)是一個(gè)函數(shù),然后返回值也是一個(gè)函數(shù)。其中作為參數(shù)的這個(gè)函數(shù)func()就在返回函數(shù)wrapper()的內(nèi)部執(zhí)行。然后在函數(shù)func()前面加上@deco,func()函數(shù)就相當(dāng)于被注入了計(jì)時(shí)功能,現(xiàn)在只要調(diào)用func(),它就已經(jīng)變身為“新的功能更多”的函數(shù)了。
所以這里裝飾器就像一個(gè)注入符號(hào):有了它,拓展了原來(lái)函數(shù)的功能既不需要侵入函數(shù)內(nèi)更改代碼,也不需要重復(fù)執(zhí)行原函數(shù)。
#帶有參數(shù)的裝飾器 import time def deco(func): def wrapper(a,b): startTime = time.time() func(a,b) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f = func f(3,4) #func()
然后你滿足了Boss的要求后,Boss又說(shuō):“小祁,我讓你拓展的函數(shù)好多可是有參數(shù)的呀,有的參數(shù)還是個(gè)數(shù)不定的那種,你的裝飾器搞的定不?”然后你嘿嘿一笑,深藏功與名!
#帶有不定參數(shù)的裝飾器 import time def deco(func): def wrapper(*args, **kwargs): startTime = time.time() func(*args, **kwargs) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) return wrapper @deco def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) @deco def func2(a,b,c): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b+c)) if __name__ == '__main__': f = func func2(3,4,5) f(3,4) #func()
最后,你的老板說(shuō):“可以的,小祁,我這里一個(gè)函數(shù)需要加入很多功能,一個(gè)裝飾器怕是搞不定,裝飾器能支持多個(gè)嘛"
最后你就把這段代碼丟給了他:
#多個(gè)裝飾器 import time def deco01(func): def wrapper(*args, **kwargs): print("this is deco01") startTime = time.time() func(*args, **kwargs) endTime = time.time() msecs = (endTime - startTime)*1000 print("time is %d ms" %msecs) print("deco01 end here") return wrapper def deco02(func): def wrapper(*args, **kwargs): print("this is deco02") func(*args, **kwargs) print("deco02 end here") return wrapper @deco01 @deco02 def func(a,b): print("hello,here is a func for add :") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f = func f(3,4) #func() ''' this is deco01 this is deco02 hello,here is a func for add : result is 7 deco02 end here time is 1003 ms deco01 end here '''
多個(gè)裝飾器執(zhí)行的順序就是從最后一個(gè)裝飾器開(kāi)始,執(zhí)行到第一個(gè)裝飾器,再執(zhí)行函數(shù)本身。
盜用評(píng)論里面一位童鞋的例子:
def dec1(func): print("1111") def one(): print("2222") func() print("3333") return one def dec2(func): print("aaaa") def two(): print("bbbb") func() print("cccc") return two @dec1 @dec2 def test(): print("test test") test()
輸出:
aaaa
1111
2222
bbbb
test test
cccc
3333
裝飾器的外函數(shù)和內(nèi)函數(shù)之間的語(yǔ)句是沒(méi)有裝飾到目標(biāo)函數(shù)上的,而是在裝載裝飾器時(shí)的附加操作。
17~20行是裝載裝飾器的過(guò)程,相當(dāng)于執(zhí)行了test=dect1(dect2(test))
,此時(shí)先執(zhí)行dect2(test)
,結(jié)果是輸出aaaa
、將func指向函數(shù)test、并返回函數(shù)two,然后執(zhí)行dect1(two),結(jié)果是輸出1111、將func指向函數(shù)two、并返回函數(shù)one,然后進(jìn)行賦值。
用函數(shù)替代了函數(shù)名test。 22行則是實(shí)際調(diào)用被裝載的函數(shù),這時(shí)實(shí)際上執(zhí)行的是函數(shù)one,運(yùn)行到func()時(shí)執(zhí)行函數(shù)two,再運(yùn)行到func()時(shí)執(zhí)行未修飾的函數(shù)test。
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Python面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python編碼操作技巧總結(jié)》及《Python入門(mén)與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
相關(guān)文章
python實(shí)現(xiàn)列表中最大最小值輸出的示例
今天小編就為大家分享一篇python實(shí)現(xiàn)列表中最大最小值輸出的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07安裝好Pycharm后如何配置Python解釋器簡(jiǎn)易教程
這篇文章主要介紹了安裝好Pycharm后如何配置Python解釋器簡(jiǎn)易教程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Python用matplotlib庫(kù)畫(huà)圖中文和負(fù)號(hào)顯示為方框的問(wèn)題解決
matplotlib中畫(huà)圖的時(shí)候會(huì)遇到負(fù)號(hào)顯示為方框的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于Python用matplotlib庫(kù)畫(huà)圖中文和負(fù)號(hào)顯示為方框的問(wèn)題解決,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07Python數(shù)據(jù)處理利器Pandas?DataFrame常用操作
這篇文章主要為大家介紹了Python數(shù)據(jù)處理利器Pandas?DataFrame,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06在Python下利用OpenCV來(lái)旋轉(zhuǎn)圖像的教程
這篇文章主要介紹了在Python下利用OpenCV來(lái)旋轉(zhuǎn)圖像的教程,代碼和核心的算法都非常簡(jiǎn)單,需要的朋友可以參考下2015-04-04OpenCV實(shí)現(xiàn)去除背景識(shí)別的方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了如何利用OpenCV實(shí)現(xiàn)去除背景識(shí)別的功能,文中為大家總結(jié)了一些方法,感興趣的小伙伴快跟隨小編一起學(xué)習(xí)一下2022-10-10