Python面試題之多個裝飾器執行順序

2022-05-10 01:08:52 字數 4415 閱讀 6221

大部分涉及多個裝飾器裝飾的函式呼叫順序時都會說明它們是自上而下的,比如下面這個例子:

def

decorator_a(func):

print

'get in decorator_a

'def inner_a(*args, **kwargs):

print

'get in inner_a

'return func(*args, **kwargs)

return

inner_a

defdecorator_b(func):

print

'get in decorator_b

'def inner_b(*args, **kwargs):

print

'get in inner_b

'return func(*args, **kwargs)

return

inner_b

@decorator_b

@decorator_a

deff(x):

print

'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 in

decorator_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的呼叫結果作為值返回。

其次得理清的乙個問題是,當裝飾器裝飾乙個函式時,究竟發生了什麼。現在簡化我們的例子,假設是下面這樣的:

def

decorator_a(func):

print

'get in decorator_a

'def inner_a(*args, **kwargs):

print

'get in inner_a

'return func(*args, **kwargs)

return

inner_a

@decorator_a

deff(x):

print

'get in f

'return x * 2

正如很多介紹裝飾器的文章裡所說:

@decorator_a

deff(x):

print

'get in f

'return x * 2

#相當於

deff(x):

print

'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裡的funcf,最後返回的是f呼叫的值,所以在最外面看起來就像直接再呼叫f一樣。

當理清上面兩方面概念時,就可以清楚地看清最原始的例子中發生了什麼。

當直譯器執行下面這段**時,實際上按照從下到上的順序已經依次呼叫了decorator_adecorator_b,這是會輸出對應的get in decorator_aget in decorator_b。 這時候f已經相當於decorator_b裡的inner_b。但因為f並沒有被呼叫,所以inner_b並沒有呼叫,依次類推inner_b內部的inner_a也沒有呼叫,所以get in inner_aget in inner_b也不會被輸出。

@decorator_b

@decorator_a

deff(x):

print

'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) ✗ python

python 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...