python裝飾器原理與用法深入詳解
本文實例講述了python裝飾器原理與用法。分享給大家供大家參考,具體如下:
你會Python嘛?
我會!
那你給我講下Python裝飾器吧!
Python裝飾器?。课覜]用過哎
以上是我一個哥們面試時候發(fā)生的真實對白。
----------------------------------------------分割線------------------------------------------------------------------------------
簡言之,python裝飾器就是用于拓展原來函數(shù)功能的一種函數(shù),這個函數(shù)的特殊之處在于它的返回值也是一個函數(shù),使用python裝飾器的好處就是在不用更改原函數(shù)的代碼前提下給函數(shù)增加新的功能。
一般而言,我們要想拓展原來函數(shù)代碼,最直接的辦法就是侵入代碼里面修改,例如:
import time
def func():
print("hello")
time.sleep(1)
print("world")
這是我們最原始的的一個函數(shù),然后我們試圖記錄下這個函數(shù)執(zhí)行的總時間,那最簡單的做法就是:
#原始侵入,篡改原函數(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在公司里面和你說:“小祁,這段代碼是我們公司的核心代碼,你不能直接去改我們的核心代碼?!蹦窃撛趺崔k呢,我們仿照裝飾器先自己試著寫一下:
#避免直接侵入原函數(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í)行,新加入功能才會生效
print("f.__name__ is",f.__name__)#f的name就是func
這里我們定義了一個函數(shù)deco,它的參數(shù)是一個函數(shù),然后給這個函數(shù)嵌入了計時功能。然后你可以拍著胸脯對老板說,看吧,不用動你原來的代碼,我照樣拓展了它的函數(shù)功能。
然后你的老板有對你說:“小祁,我們公司核心代碼區(qū)域有一千萬個func()函數(shù),從func01()到func1kw(),按你的方案,想要拓展這一千萬個函數(shù)功能,就是要執(zhí)行一千萬次deco()函數(shù),這可不行呀,我心疼我的機器?!?/p>
好了,你終于受夠你老板了,準備辭職了,然后你無意間聽到了裝飾器這個神器,突然發(fā)現(xiàn)能滿足你閆博士的要求了。
我們先實現(xiàn)一個最簡陋的裝飾器,不使用任何語法糖和高級語法,看看裝飾器最原始的面貌:
#既不需要侵入,也不需要函數(shù)重復執(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ù)是一個函數(shù),然后返回值也是一個函數(shù)。其中作為參數(shù)的這個函數(shù)func()就在返回函數(shù)wrapper()的內部執(zhí)行。然后在函數(shù)func()前面加上@deco,func()函數(shù)就相當于被注入了計時功能,現(xiàn)在只要調用func(),它就已經(jīng)變身為“新的功能更多”的函數(shù)了。
所以這里裝飾器就像一個注入符號:有了它,拓展了原來函數(shù)的功能既不需要侵入函數(shù)內更改代碼,也不需要重復執(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又說:“小祁,我讓你拓展的函數(shù)好多可是有參數(shù)的呀,有的參數(shù)還是個數(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()
最后,你的老板說:“可以的,小祁,我這里一個函數(shù)需要加入很多功能,一個裝飾器怕是搞不定,裝飾器能支持多個嘛"
最后你就把這段代碼丟給了他:
#多個裝飾器
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
'''
多個裝飾器執(zhí)行的順序就是從最后一個裝飾器開始,執(zhí)行到第一個裝飾器,再執(zhí)行函數(shù)本身。
盜用評論里面一位童鞋的例子:
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ù)和內函數(shù)之間的語句是沒有裝飾到目標函數(shù)上的,而是在裝載裝飾器時的附加操作。
17~20行是裝載裝飾器的過程,相當于執(zhí)行了test=dect1(dect2(test)),此時先執(zhí)行dect2(test),結果是輸出aaaa、將func指向函數(shù)test、并返回函數(shù)two,然后執(zhí)行dect1(two),結果是輸出1111、將func指向函數(shù)two、并返回函數(shù)one,然后進行賦值。
用函數(shù)替代了函數(shù)名test。 22行則是實際調用被裝載的函數(shù),這時實際上執(zhí)行的是函數(shù)one,運行到func()時執(zhí)行函數(shù)two,再運行到func()時執(zhí)行未修飾的函數(shù)test。
更多關于Python相關內容感興趣的讀者可查看本站專題:《Python面向對象程序設計入門與進階教程》、《Python數(shù)據(jù)結構與算法教程》、《Python函數(shù)使用技巧總結》、《Python字符串操作技巧匯總》、《Python編碼操作技巧總結》及《Python入門與進階經(jīng)典教程》
希望本文所述對大家Python程序設計有所幫助。
相關文章
Python用matplotlib庫畫圖中文和負號顯示為方框的問題解決
matplotlib中畫圖的時候會遇到負號顯示為方框的問題,下面這篇文章主要給大家介紹了關于Python用matplotlib庫畫圖中文和負號顯示為方框的問題解決,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-07-07
Python數(shù)據(jù)處理利器Pandas?DataFrame常用操作
這篇文章主要為大家介紹了Python數(shù)據(jù)處理利器Pandas?DataFrame,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06

