python裝飾器的詳細解析
阿新 • • 發佈:2020-10-10
寫在前面:
python裝飾器(fuctional decorators)就是用於拓展原來函式功能的一種函式,目的是在不改變原函式名(或類名)的情況下,給函式增加新的功能。這個函式的特殊之處在於它的返回值也是一個函式,這個函式是內嵌“原“”函式的函式。
一般而言,我們要想拓展原來函式程式碼,最直接的辦法就是侵入程式碼裡面修改,例如:
import time
def f():
print("hello")
time.sleep(1)
print("world")
這是我們最原始的的一個函式,然後我們試圖記錄下這個函式執行的總時間,那最簡單的做法就是改動原來的程式碼:
import time
def f():
start_time = time.time()
print("hello")
time.sleep(1)
print("world")
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" %execution_time)
但是實際工作中,有些時候核心程式碼並不可以直接去改,所以在不改動原始碼的情況下,我們可以再定義一個函式。(但是生效需要再次執行函式)
import time
def deco(func):
start_time = time.time()
func()
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" %execution_time)
def f():
print("hello")
time.sleep(1)
print("world")
if __name__ == '__main__':
deco(f)
print("f.__name__ is ",f.__name__)
print()
這裡我們定義了一個函式deco,它的引數是一個函式,然後給這個函式嵌入了計時功能。但是想要拓展這一千萬個函式功能,就是要執行一千萬次deco()函式,所以這樣並不理想!
接下來,我們可以試著用裝飾器來實現,先看看裝飾器最原始的面貌。
import time
def deco(f):
def wrapper():
start_time = time.time()
f()
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" %execution_time )
return wrapper
@deco
def f():
print("hello")
time.sleep(1)
print("world")
if __name__ == '__main__':
f()
這裡的deco函式就是最原始的裝飾器,它的引數是一個函式,然後返回值也是一個函式。
其中作為引數的這個函式f()就在返回函式wrapper()的內部執行。然後在函式f()前面加上@deco,
f()函式就相當於被注入了計時功能,現在只要呼叫f(),它就已經變身為“新的功能更多”的函數了(不需要重複執行原函式)。
擴充套件1:帶有固定引數的裝飾器
import time
def deco(f):
def wrapper(a,b):
start_time = time.time()
f(a,b)
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" % execution_time)
return wrapper
@deco
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f(3,4)
擴充套件2:無固定引數的裝飾器
import time
def deco(f):
def wrapper(*args, **kwargs):
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time_ = (end_time - start_time)*1000
print("time is %d ms" %execution_time)
return wrapper
@deco
def f(a,b):
print("be on")
time.sleep(1)
print("result is %d" %(a+b))
@deco
def f2(a,b,c):
print("be on")
time.sleep(1)
print("result is %d" %(a+b+c))
if __name__ == '__main__':
f2(3,4,5)
f(3,4)
擴充套件3:使用多個裝飾器,裝飾一個函式
import time
def deco01(f):
def wrapper(*args, **kwargs):
print("this is deco01")
start_time = time.time()
f(*args, **kwargs)
end_time = time.time()
execution_time = (end_time - start_time)*1000
print("time is %d ms" % execution_time)
print("deco01 end here")
return wrapper
def deco02(f):
def wrapper(*args, **kwargs):
print("this is deco02")
f(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def f(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f(3,4)
'''
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
'''
裝飾器呼叫順序
裝飾器是可以疊加使用的,那麼使用裝飾器以後程式碼是啥順序呢?
對於Python中的”@”語法糖,裝飾器的呼叫順序與使用 @ 語法糖宣告的順序相反。在這個例子中,”f(3, 4) = deco01(deco02(f(3, 4)))”。