Python裝飾器-裝飾流程,執行順序

最近看到一個關於Flask的CTF(RealWorld CTF 2018 web題bookhub)文章
其中的一個trick是裝飾器的順序問題,就想寫篇博客回顧下裝飾器~html

首先強烈推薦好久以前看的一篇博文
(翻譯)理解PYTHON中的裝飾器
關於什麼是裝飾器看這篇文章就行了~
這裏主要想寫關於多個裝飾器的執行流程

裝飾順序

示例代碼python

# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
pass
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
pass
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    pass

# 輸出結果
functionTwo初始化
functionOne初始化
從上面咱們能得知: 裝飾順序,就近裝飾
而後咱們利用下面的代碼進行一步探究
以下咱們得知:執行這段代碼,至關於:
首先,將testFunction函數打包給wrapperTwo,因爲沒有調用,functionTwo總體返回了wrapperTwo,而沒有執行
而後,functionOne將wrapperTwo做爲參數,打包成wrapperOne
# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
print("第一處"+function_to_decorate.__name__)
function_to_decorate()
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
print("第二處"+function_to_decorate.__name__)
function_to_decorate()
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    print('index')

testFunction()

#輸出結果
functionTwo初始化
functionOne初始化
第一處wrapperTwo
第二處testFunction
index

執行順序

從上面的第二段代碼咱們已經能看出部分執行順序了
就是 它會優先執行咱們打包好的wrapperOne,由於從起始的testFunction,wrapperTwo都已經打包在wrapperOne
能夠說成 執行順序,就遠執行
咱們繼續執行下面的代碼:
# import pdb;pdb.set_trace()

def functionOne(function_to_decorate):
    print("functionOne初始化")
  
    def wrapperOne():
print("第一處"+function_to_decorate.__name__)
function_to_decorate()
print("wrapperOne")
    return wrapperOne

def functionTwo(function_to_decorate):
    print("functionTwo初始化")
  
    def wrapperTwo():
print("第二處"+function_to_decorate.__name__)
function_to_decorate()
print("wrapperTwo")
    return wrapperTwo

@functionOne
@functionTwo
def testFunction():
    print('index')

testFunction()

# 輸出結果
functionTwo初始化
functionOne初始化
第一處wrapperTwo
第二處testFunction
index
wrapperTwo
wrapperOne
這個執行順序可能也困擾了不少人,如今咱們從輸出結果看
對照代碼,就很容易清楚了,執行到wrapperOne中的function_to_decorate時
其實至關於跳轉到了函數wrapperTwo,而後執行wrapperTwo

Flask @login_require

從上面的幾個例子咱們應該大概瞭解了,多個裝飾器進行裝飾以及執行的順序
咱們來看這道CTF題目,咱們首先須要知道的是Flask中路由就是一個裝飾
from flask import Flask

app = Flask(__name__)
app.debug = True

# import pdb;pdb.set_trace()

# 爲了更好的控制輸出,自定義了loginRequire裝飾器
def loginRequire(function_to_decorate):
    print("loginRequire初始化")
  
    def wrapperTwo():
print("loginRequire裝飾成功")
print(function_to_decorate.__name__)
return function_to_decorate()
    return wrapperTwo

@loginRequire
@app.route('/up')
def up():
    return "裝飾路由放在上面!"

@app.route('/down')
@loginRequire
def down():
    return "裝飾路由放在下面!"

if __name__ == '__main__':
    app.run()

# 分別訪問兩個url輸出結果
loginRequire初始化
loginRequire初始化
 * Debugger is active!
 * Debugger PIN: 244-957-971
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [24/Aug/2018 19:01:30] "GET /up HTTP/1.1" 200 -
loginRequire裝飾成功
down
127.0.0.1 - - [24/Aug/2018 19:01:35] "GET /down HTTP/1.1" 200 -
從輸出結果咱們能清楚的看到up的裝飾,並無執行裝飾器
若是按照咱們上面的分析,不管在上面仍是下面都會執行的啊??只是順序不一樣罷了~
咱們利用pdb來一步步調試查看哪裏的問題,部分log以下:
> c:\users\bayi\desktop\test\256.py(17)<module>()
-> @loginRequire
(Pdb) s
> c:\users\bayi\desktop\test\256.py(18)<module>()
-> @app.route('/up')
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1252)route()-><function Fla...at 0x0376F978>
-> return decorator
(Pdb) s
--Call--
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1248)decorator()
-> def decorator(f):
(Pdb) f
<function up at 0x0376F9C0>
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1249)decorator()
-> endpoint = options.pop('endpoint', None)
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1250)decorator()
-> self.add_url_rule(rule, endpoint, f, **options)
(Pdb) f
<function up at 0x0376F9C0>
#===================================================================================#
        上方up 下方down
#===================================================================================#
> c:\users\bayi\desktop\test\256.py(22)<module>()
-> @app.route('/down')
(Pdb) s
> c:\users\bayi\desktop\test\256.py(23)<module>()
-> @loginRequire
(Pdb) s
--Call--
> c:\users\bayi\desktop\test\256.py(6)loginRequire()
-> def loginRequire(function_to_decorate):
(Pdb) s
> c:\users\bayi\desktop\test\256.py(7)loginRequire()
-> print("loginRequire初始化")
(Pdb) s
loginRequire初始化
> c:\users\bayi\desktop\test\256.py(9)loginRequire()
-> def wrapperTwo():
(Pdb) s
> c:\users\bayi\desktop\test\256.py(13)loginRequire()
-> return wrapperTwo
(Pdb) s
--Return--
> c:\users\bayi\desktop\test\256.py(13)loginRequire()-><function log...at 0x0071C468>
-> return wrapperTwo
(Pdb) s
--Call--
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1248)decorator()
-> def decorator(f):
(Pdb) f
<function loginRequire.<locals>.wrapperTwo at 0x0071C468>
從上面的執行流程,打印出不斷出現的f,咱們能看出,兩個順序的f值不一樣
在up中,f=up()
在down中,f=wrapperTwo()
這點符合預期,裝飾位置不一樣,然而在執行Flask源碼 add_url_rule時
如上面log所示,直接添加了f的值
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1249)decorator()
-> endpoint = options.pop('endpoint', None)
(Pdb) s
> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1250)decorator()
-> self.add_url_rule(rule, endpoint, f, **options)
(Pdb) f
<function up at 0x0376F9C0>

也就是添加路由的時候會選擇丟失外層的路由,只裝飾route下方的函數
在add_url_rule中,有這段註釋:web

Basically this example::

    @app.route('/')
    def index():
        pass

Is equivalent to the following::

    def index():
        pass
    app.add_url_rule('/', 'index', index)

博客地址shell

相關文章
相關標籤/搜索