摘要:最近看到一個關于的題文章其中的一個是裝飾器的順序問題就想寫篇博客回顧下裝飾器首先強烈推薦很久之前看的一篇博文翻譯理解中的裝飾器關于什么是裝飾器看這篇文章就好了這里主要想寫關于多個裝飾器的執行流程裝飾順序示例代碼初始化初始化輸出結果初始化初始
最近看到一個關于Flask的CTF(RealWorld CTF 2018 web題bookhub)文章
其中的一個trick是裝飾器的順序問題,就想寫篇博客回顧下裝飾器~
首先強烈推薦很久之前看的一篇博文裝飾順序
(翻譯)理解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
這個執行順序可能也困擾了很多人,現在我們從輸出結果看Flask @login_require
對照代碼,就很容易清楚了,執行到wrapperOne中的function_to_decorate時
其實相當于跳轉到了函數wrapperTwo,然后執行wrapperTwo
從上面的幾個例子我們應該大概了解了,多個裝飾器進行裝飾以及執行的順序
我們來看這道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:usersayidesktop est256.py(17)() -> @loginRequire (Pdb) s > c:usersayidesktop est256.py(18) () -> @app.route("/up") (Pdb) s > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1252)route()-> -> return decorator (Pdb) s --Call-- > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1248)decorator() -> def decorator(f): (Pdb) f (Pdb) s > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1249)decorator() -> endpoint = options.pop("endpoint", None) (Pdb) s > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1250)decorator() -> self.add_url_rule(rule, endpoint, f, **options) (Pdb) f #===================================================================================# 上方up 下方down #===================================================================================# > c:usersayidesktop est256.py(22) () -> @app.route("/down") (Pdb) s > c:usersayidesktop est256.py(23) () -> @loginRequire (Pdb) s --Call-- > c:usersayidesktop est256.py(6)loginRequire() -> def loginRequire(function_to_decorate): (Pdb) s > c:usersayidesktop est256.py(7)loginRequire() -> print("loginRequire初始化") (Pdb) s loginRequire初始化 > c:usersayidesktop est256.py(9)loginRequire() -> def wrapperTwo(): (Pdb) s > c:usersayidesktop est256.py(13)loginRequire() -> return wrapperTwo (Pdb) s --Return-- > c:usersayidesktop est256.py(13)loginRequire()-> -> return wrapperTwo (Pdb) s --Call-- > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1248)decorator() -> def decorator(f): (Pdb) f .wrapperTwo at 0x0071C468>
從上面的執行流程,打印出不斷出現的f,我們能看出,兩個順序的f值不同
在up中,f=up()
在down中,f=wrapperTwo()
這點符合預期,裝飾位置不同,然而在執行Flask源碼 add_url_rule時
如上面log所示,直接添加了f的值
> c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1249)decorator() -> endpoint = options.pop("endpoint", None) (Pdb) s > c:usersayi.virtualenvs est-gq7eoxbqlibsite-packagesflaskapp.py(1250)decorator() -> self.add_url_rule(rule, endpoint, f, **options) (Pdb) f
也就是添加路由的時候會選擇丟失外層的路由,只裝飾route下方的函數
在add_url_rule中,有這段注釋:
Basically this example:: @app.route("/") def index(): pass Is equivalent to the following:: def index(): pass app.add_url_rule("/", "index", index)
博客地址
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/42289.html
摘要:裝飾器的使用符合了面向對象編程的開放封閉原則。三簡單的裝飾器基于上面的函數執行時間的需求,我們就手寫一個簡單的裝飾器進行實現。函數體就是要實現裝飾器的內容。類裝飾器的實現是調用了類里面的函數。類裝飾器的寫法比我們裝飾器函數的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...
摘要:如果不使用裝飾器的話,普通的做法可能是在中寫一堆校驗代碼來判斷用戶是否登錄,然后決定后面的執行邏輯,這樣比較麻煩。 前言 裝飾器是程序開發中經常會用到的一個功能,也是python語言開發的基礎知識,如果能夠在程序中合理的使用裝飾器,不僅可以提高開發效率,而且可以讓寫的代碼看上去顯的高大上^_^ 使用場景 可以用到裝飾器的地方有很多,簡單的舉例如以下場景 引入日志 函數執行時間統計 執...
摘要:探究多個裝飾器執行順序裝飾器是用于封裝函數或代碼的工具,網上可以搜到很多文章可以學習,我在這里要討論的是多個裝飾器執行順序的一個迷思。這時候你該知道為什么輸出結果會是那樣,以及對裝飾器執行順序實際發生了什么有一定了解了吧。 探究多個裝飾器執行順序 裝飾器是Python用于封裝函數或代碼的工具,網上可以搜到很多文章可以學習,我在這里要討論的是多個裝飾器執行順序的一個迷思。 疑問 大部...
摘要:在這種代碼運行期間動態增加功能的方式,稱之為裝飾器。四接收特定類型參數的裝飾器裝飾器可以接收參數,當調用裝飾器返回的函數時,也就調用了包裹函數,把參數傳入包裹函數,它將參數傳遞給被裝飾的函數。執行結果執行結果 【題外話】心塞塞 心情down down down 有段時間沒用裝飾器了,然后然后問著就跪了~~~回來翻了翻資料和代碼...... 一、什么是裝飾器 裝飾器,decor...
摘要:上下文管理器協議包含和兩個方法。因此必要時在上下文管理器函數中使用語句防范錯誤。構建臨時忽略指定異常的上下文管理器。這是個基類,用于定義基于類的上下文管理器。塊結束時,按照后進先出的順序調用棧中各個上下文管理器的方法。 導語:本文章記錄了本人在學習Python基礎之控制流程篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、掌握if語句之外的el...
閱讀 2768·2021-11-24 09:39
閱讀 1657·2021-09-28 09:35
閱讀 1133·2021-09-06 15:02
閱讀 1330·2021-07-25 21:37
閱讀 2740·2019-08-30 15:53
閱讀 3657·2019-08-30 14:07
閱讀 725·2019-08-30 11:07
閱讀 3532·2019-08-29 18:36