一、什麼是裝飾器
裝飾器就是用來為被修飾對象添加上新功能的工具
注意:裝飾器本身可以是任意可調用對象,被裝飾器的對象也可以是任意可調用對象
那為什麼要用裝飾器呢?
這裡我們需要知道開放封閉原則:封閉指的是對修改封閉,對擴充開放
裝飾器的實作必須遵循兩大原則:
1.不修改被裝飾對象的源代碼
2.不修改被裝飾對象的調用方式
裝飾的目标:就是在滿足1和2的前提下為被修飾對象添加上新功能
簡單的裝飾器:
import time
def index():
print('welcome to index page')
time.sleep(3)
def outter(func): # func=最原始那個index的記憶體位址
def wrapper():
start=time.time()
func() #最原始的那個index的記憶體位址()
stop=time.time()
print('run time is %s' %(stop - start))
return wrapper
index=outter(index) #index=outter(最原始那個index的記憶體位址) #index=wrapper的記憶體位址
index() #wrapper的記憶體位址()
上面這種裝飾器沒有傳入參數,下面對他進行更新
def home(name):
print('welcome %s to home page' %name)
time.sleep(2)
return 123
def timmer(func): #func=最原始那個home函數的内位址
def wrapper(*args,**kwargs): #args=('egon',) kwargs={}
start=time.time()
res=func(*args,**kwargs) #最原始那個home函數的内位址('egon')
stop=time.time()
print('run time is %s' %(stop - start))
return res
return wrapper
home=timmer(home) #home=outter(最原始那個home函數的内位址) #home=wrapper函數的内位址
res=home('egon') # res=wrapper函數的内位址('egon')
參數是可以傳入了,那還有沒有可以優化的地方呢,這個時候就用到了python的文法糖,
def outter(func):
def wrapper(*args,**kwargs):
#在調用函數前加功能
res=func(*args,**kwargs) #調用被裝飾的\也就是最原始的那個函數
#在調用函數後加功能
return res
return wrapper
@outter #index=outter(index) #index=wrapper
def index():
print('welcome to index page')
time.sleep(3)
index()
裝飾器的文法糖:在被裝飾對象正上方單獨一行寫@裝飾器的名字
運作原理:python解釋器一旦運作到@裝飾器的名字,就會調用裝飾器,然後将被裝飾函數的記憶體位址當做參數傳給裝飾器,最後将裝飾器調用的結果指派給原函數名
調用多個裝飾器:
def outter1(func1): #func1=wrapper2
print('outter1')
def wrapper1(*args,**kwargs):
print('wrapper1')
res1=func1(*args,**kwargs) #res1=wrapper2(*args,**kwargs)
return res1
return wrapper1
def outter2(func2): #func2=最原始的那個index的記憶體位址
print('outter2')
def wrapper2(*args,**kwargs):
print('wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
@outter1 # index=outter1(wrapper2) #index=wrapper1
@outter2 #outter2(最原始的那個index的記憶體位址) ===> wrapper2
def index():
print('welcome to index page')
time.sleep(3)
index() #wrapper1()
'''
outter2
outter1
wrapper1
wrapper2
'''
解釋順序是自下往上的,執行順序從上往下,那列印的結果和我們預想的不一樣呢,我們應該了解裝飾器函數在被裝飾器函數定義好後立即執行
模闆:
# 有參裝飾器的模闆
def outter1(x,y,z):
def outter2(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
return outter2
# 無參裝飾器的模闆
def outter(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
return res
return wrapper
補充:
調用方式和源代碼都沒有修改,是不是所有都很完美了呢?不是其實還有一個需要注釋的地方就是注釋文檔
def outter(func):
# @wraps(func)
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return res
return inner
@outter
def index():
'''
zfj
1212
1231
'''
# print('1111')
index()
print(index.__name__)# inner
print(index.__doc__)#None
index的函數名和文檔都變成inner的了,沒有和原來的index一樣,這是我們可以在inner函數中加入
inner.__name__=func.__name__
inner.__doc__= func.__doc__
這樣index得到的文檔就是原來的樣子了,還有一個方法是使用wraps!
from functools import wraps
def outter(func):
@wraps(func)
def inner(*args,**kwargs):
res = func(*args,**kwargs)
return res
# inner.__name__=func.__name__
# inner.__doc__= func.__doc__
return inner
@outter
def index():
'''
zfj
1212
1231
'''
# print('1111')
index()
print(index.__name__)
print(index.__doc__)
很感謝Nisen的裝飾器執行順序迷思,對我的啟發很大
https://segmentfault.com/a/1190000007837364
焚膏油以繼晷,恒兀兀以窮年。