python 裝飾器 用法
關於裝飾器,找到一篇通俗易懂的文章,mark下
簡言之,python裝飾器就是用於拓展原來函式功能的一種函式,這個函式的特殊之處在於它的返回值也是一個函式,使用python裝飾器的好處就是在不用更改原函式的程式碼前提下給函式增加新的功能。
一般而言,我們要想拓展原來函式程式碼,最直接的辦法就是侵入程式碼裡面修改,例如:
import time
def func():
print("hello")
time.sleep(1)
print("world")
這是我們最原始的的一個函式,然後我們試圖記錄下這個函式執行的總時間,那最簡單的做法就是:
#原始侵入,篡改原函式
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在公司裡面和你說:“小祁,這段程式碼是我們公司的核心程式碼,你不能直接去改我們的核心程式碼。”那該怎麼辦呢,我們仿照裝飾器先自己試著寫一下:
#避免直接侵入原函式修改,但是生效需要再次執行函式
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()作為引數執行,新加入功能才會生效
print("f.__name__ is",f.__name__)#f的name就是func()
print()
#func()
這裡我們定義了一個函式deco,它的引數是一個函式,然後給這個函式嵌入了計時功能。然後你可以拍著胸脯對老闆說,看吧,不用動你原來的程式碼,我照樣拓展了它的函式功能。
然後你的老闆有對你說:“小祁,我們公司核心程式碼區域有一千萬個func()函式,從func01()到func1kw(),按你的方案,想要拓展這一千萬個函式功能,就是要執行一千萬次deco()函式,這可不行呀,我心疼我的機器。”
好了,你終於受夠你老闆了,準備辭職了,然後你無意間聽到了裝飾器這個神器,突然發現能滿足你閆博士的要求了。
我們先實現一個最簡陋的裝飾器,不使用任何語法糖和高階語法,看看裝飾器最原始的面貌:
#既不需要侵入,也不需要函式重複執行
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,執行f()就是執行func()
f()
這裡的deco函式就是最原始的裝飾器,它的引數是一個函式,然後返回值也是一個函式。其中作為引數的這個函式func()就在返回函式wrapper()的內部執行。然後在函式func()前面加上@deco,func()函式就相當於被注入了計時功能,現在只要呼叫func(),它就已經變身為“新的功能更多”的函數了。
所以這裡裝飾器就像一個注入符號:有了它,拓展了原來函式的功能既不需要侵入函式內更改程式碼,也不需要重複執行原函式。
#帶有引數的裝飾器
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又說:“小祁,我讓你拓展的函式好多可是有引數的呀,有的引數還是個數不定的那種,你的裝飾器搞的定不?”然後你嘿嘿一笑,深藏功與名!
#帶有不定引數的裝飾器
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()
最後,你的老闆說:“可以的,小祁,我這裡一個函式需要加入很多功能,一個裝飾器怕是搞不定,裝飾器能支援多個嘛”
最後你就把這段程式碼丟給了他:
#多個裝飾器
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
'''
多個裝飾器執行的順序就是從最後一個裝飾器開始,執行到第一個裝飾器,再執行函式本身。
盜用評論裡面一位童鞋的例子:
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