大部分涉及多個裝飾器裝飾的函式呼叫順序時都會說明它們是自上而下的,比如下面這個例子:
defdecorator_a(func):
'get in decorator_a
'def inner_a(*args, **kwargs):
'get in inner_a
'return func(*args, **kwargs)
return
inner_a
defdecorator_b(func):
'get in decorator_b
'def inner_b(*args, **kwargs):
'get in inner_b
'return func(*args, **kwargs)
return
inner_b
@decorator_b
@decorator_a
deff(x):
'get in f
'return x * 2f(1)
上面**先定義裡兩個函式:decotator_a, decotator_b
, 這兩個函式實現的功能是,接收乙個函式作為引數然後返回建立的另乙個函式,在這個建立的函式裡呼叫接收的函式(文字比**繞人)。最後定義的函式f
採用上面定義的decotator_a, decotator_b
作為裝飾函式。在當我們以1為引數呼叫裝飾後的函式f
後,decotator_a, decotator_b
的順序是什麼呢(這裡為了表示函式執行的先後順序,採用列印輸出的方式來檢視函式的執行順序)?
如果不假思索根據自下而上的原則來判斷地話,先執行decorator_a
再執行decorator_b
, 那麼會先輸出get in decotator_a
,get in inner_a
再輸出get in decotator_b
,get in inner_b
。然而事實並非如此。
實際上執行的結果如下:
get indecorator_a
get
indecorator_b
get
ininner_b
get
ininner_a
get
in f
為什麼是先執行inner_b
再執行inner_a
呢?為了徹底看清上面的問題,得先分清兩個概念:函式和函式呼叫。上面的例子中f
稱之為函式,f(1)
稱之為函式呼叫,後者是對前者傳入引數進行求值的結果。在python中函式也是乙個物件,所以f
是指代乙個函式物件,它的值是函式本身,f(1)
是對函式的呼叫,它的值是呼叫的結果,這裡的定義下f(1)
的值2。同樣地,拿上面的decorator_a
函式來說,它返回的是個函式物件inner_a
,這個函式物件是它內部定義的。在inner_a
裡呼叫了函式func
,將func
的呼叫結果作為值返回。
其次得理清的乙個問題是,當裝飾器裝飾乙個函式時,究竟發生了什麼。現在簡化我們的例子,假設是下面這樣的:
defdecorator_a(func):
'get in decorator_a
'def inner_a(*args, **kwargs):
'get in inner_a
'return func(*args, **kwargs)
return
inner_a
@decorator_a
deff(x):
'get in f
'return x * 2
正如很多介紹裝飾器的文章裡所說:
@decorator_adeff(x):
'get in f
'return x * 2
#相當於
deff(x):
'get in f
'return x * 2f = decorator_a(f)
所以,當直譯器執行這段**時,decorator_a
已經呼叫了,它以函式f
作為引數, 返回它內部生成的乙個函式,所以此後f
指代的是decorater_a
裡面返回的inner_a
。所以當以後呼叫f
時,實際上相當於呼叫inner_a
,傳給f
的引數會傳給inner_a
, 在呼叫inner_a
時會把接收到的引數傳給inner_a
裡的func
即f
,最後返回的是f
呼叫的值,所以在最外面看起來就像直接再呼叫f
一樣。
當理清上面兩方面概念時,就可以清楚地看清最原始的例子中發生了什麼。
當直譯器執行下面這段**時,實際上按照從下到上的順序已經依次呼叫了decorator_a
和decorator_b
,這是會輸出對應的get in decorator_a
和get in decorator_b
。 這時候f
已經相當於decorator_b
裡的inner_b
。但因為f
並沒有被呼叫,所以inner_b
並沒有呼叫,依次類推inner_b
內部的inner_a
也沒有呼叫,所以get in inner_a
和get in inner_b
也不會被輸出。
@decorator_b@decorator_a
deff(x):
'get in f
'return x * 2
然後最後一行當我們對f
傳入引數1進行呼叫時,inner_b
被呼叫了,它會先列印get in inner_b
,然後在inner_b
內部呼叫了inner_a
所以會再列印get in inner_a
, 然後再inner_a
內部呼叫的原來的f
, 並且將結果作為最終的返回。這時候你該知道為什麼輸出結果會是那樣,以及對裝飾器執行順序實際發生了什麼有一定了解了吧。
當我們在上面的例子最後一行f
的呼叫去掉,放到repl裡演示,也能很自然地看出順序問題:
➜ test git:(master) ✗ pythonpython 2.7.11 (default, jan 22 2016, 08:29:18)
type
"help
", "
", "
credits"or
"license
"for
more information.
>>> import
test13
get
indecorator_a
get
indecorator_b
>>> test13.f(1)
get
ininner_b
get
ininner_a
get inf
2>>> test13.f(2)
get
ininner_b
get
ininner_a
get inf
4>>>
在實際應用的場景中,當我們採用上面的方式寫了兩個裝飾方法比如先驗證有沒有登入@login_required
, 再驗證許可權夠不夠時@permision_allowed
時,我們採用下面的順序來裝飾函式:
@login_required@permision_allowed
deff() #
do something
return
Python閉包and裝飾器之多個裝飾器的使用
def make div func 對被裝飾的函式的返回值 div標籤 def inner args,kwargs return func return inner def make p func 對被裝飾的函式的返回值 p標籤 def inner args,kwargs return func r...
python語法面試題 python面試題
1.去重,集合 集合的乙個重要特點是 自動去除重複的值 li 1,2,3,1,1,2,2,3,3 去除重複的元素 set set li 轉換為集合,因為集合會自動去重。print set li list set 將集合轉換為列表print li 2.生成器 規則 生成器函式,或者生成器表示式,在呼叫...
面試題程式設計題15 python 裝飾器簡單例項
from time import sleep import time python裝飾器庫 functools from functools import wraps 裝飾普通函式 defdecorator fun wraps fun def start time time.time fun end...