當一個被裝飾的對象同時疊加多個裝飾器時
裝飾器的加載順序是:由下而上
裝飾器的執行順序是:由上而下
加載裝飾器就是將原函數名與裝飾器內部的wrapper函數進行偷樑換柱
執行裝飾器實際上就是執行裝飾器內部的wrapper函數。
我們來看下面這段代碼
我們定義了兩個裝飾器:無參裝飾器timmer與有參裝飾器auth
我們用這兩個裝飾器去修飾index函數。讓我們看看到底是發生了一件什麼事
import time
def timmer(func):
def wrapper1(*args, **kwargs):
print('===================================>wrapper1運行了')
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print('run time is %s' % (stop - start))
return res
return wrapper1
def auth(engine='file'):
def xxx(func):
def wrapper2(*args, **kwargs):
print('===================================>wrapper2運行了')
name = input('username>>>: ').strip()
pwd = input('password>>>: ').strip()
if engine == 'file':
print('基於文件的認證')
if name == 'egon' and pwd == '123':
print('login successfull')
res = func(*args, **kwargs)
return res
elif engine == 'mysql':
print('基於mysql的認證')
elif engine == 'ldap':
print('基於ldap的認證')
else:
print('錯誤的認證源')
return wrapper2
return xxx
@timmer
@auth(engine='file')
def index():
print('welcome to index page')
time.sleep(2)
index()
文章一開頭,我們就已經說了,裝飾器的加載順序是由下而上的。就是說離被裝飾函數最近的裝飾器先加載。
那麼在這段代碼中可以看到,是@auth(engine='file')先被加載,在加載的時候,python一遇到函數名+()就會觸發函數的執行,也就是說,加載的第一步就是執行了auth(engine='file')函數。
我們拐過頭來去看auth函數的代碼,執行auth函數其實就幹了兩件事,定義一個叫xxx的函數,然後將xxx函數內存地址作爲返回值返回。沒有調用xxx就不會觸發xxx函數的執行。
auth(engine='file')函數執行完畢以後,語法糖:'@'符號開始生效,它的作用就是將正下方的函數名(函數內存地址)作爲參數傳入裝飾器並將原函數名作爲變量名去接收。
在這裏就是幹了這樣一件事:index=xxx(index)(這裏需要注意。第一:爲什麼是xxx呢?因爲在auth函數執行完畢後把xxx函數的內存地址作爲返回值返回了。所以這裏是xxx。第二:括號內的index和等號左邊的index不是同一個東西,括號內的index是原函數index的內存地址,等號左邊的index就是一個變量名。)然後其實現在代碼就是變成了下述代碼的樣子,只是這是在後臺自動做的,我們看不到。這些都是Python解釋器默默地幫我們實現的。
@timmer
index = xxx(index) # index = wrapper2函數內存地址
def index():
print('welcome to index page')
time.sleep(2)
我們看現在的代碼,xxx函數加(),一看到函數名+()就會執行函數,返回上面代碼去看,執行xxx函數幹了兩件事,幹了兩件事,定義了wrapper2函數,然後將wrapper2函數內存地址作爲返回值返回。所以,變量名index是指向wrapper2函數的內存地址。
然後再向上加載timmer裝飾器。函數名沒有加(),則執行@語法。@的作用咱們上面說過了,忘掉的可以再看看。index這個變量名,指向的是wrapper2函數,將index函數名傳入timmer函數,還是做了兩件事,定義wrapper1函數,將wrapper1函數的內存地址作爲返回值返回。這個時候已經變成了這個樣子
index = wrapper1(index)
index = xxx(index) # index = wrapper2函數內存地址
def index():
print('welcome to index page')
time.sleep(2)
這個時候的第一行中,括號內的index指向的是wrapper2函數的內存地址,等號左邊的index是一個新的變量名。這時的index指向的是wrapper1函數的內存地址。
以上就是裝飾器加載時python解釋器在背後默默幫我們做的事。
執行的時候會按照順序依次執行,先執行wrapper1函數,再執行wrapper2函數,再執行被裝飾函數。
這就是我們上面所說的: