摘要:最近看到一个关于的题文章其中的一个是装饰器的顺序问题就想写篇博客回顾下装饰器首先强烈推荐很久之前看的一篇博文翻译理解中的装饰器关于什么是装饰器看这篇文章就好了这里主要想写关于多个装饰器的执行流程装饰顺序示例代码初始化初始化输出结果初始化初始
最近看到一个关于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)
博客地址
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42289.html
摘要:装饰器的使用符合了面向对象编程的开放封闭原则。三简单的装饰器基于上面的函数执行时间的需求,我们就手写一个简单的装饰器进行实现。函数体就是要实现装饰器的内容。类装饰器的实现是调用了类里面的函数。类装饰器的写法比我们装饰器函数的写法更加简单。 目录 前言 一、什么是装饰器 二、为什么要用装饰器 ...
摘要:如果不使用装饰器的话,普通的做法可能是在中写一堆校验代码来判断用户是否登录,然后决定后面的执行逻辑,这样比较麻烦。 前言 装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的地方有很多,简单的举例如以下场景 引入日志 函数执行时间统计 执...
摘要:探究多个装饰器执行顺序装饰器是用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思。这时候你该知道为什么输出结果会是那样,以及对装饰器执行顺序实际发生了什么有一定了解了吧。 探究多个装饰器执行顺序 装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思。 疑问 大部...
摘要:在这种代码运行期间动态增加功能的方式,称之为装饰器。四接收特定类型参数的装饰器装饰器可以接收参数,当调用装饰器返回的函数时,也就调用了包裹函数,把参数传入包裹函数,它将参数传递给被装饰的函数。执行结果执行结果 【题外话】心塞塞 心情down down down 有段时间没用装饰器了,然后然后问着就跪了~~~回来翻了翻资料和代码...... 一、什么是装饰器 装饰器,decor...
摘要:上下文管理器协议包含和两个方法。因此必要时在上下文管理器函数中使用语句防范错误。构建临时忽略指定异常的上下文管理器。这是个基类,用于定义基于类的上下文管理器。块结束时,按照后进先出的顺序调用栈中各个上下文管理器的方法。 导语:本文章记录了本人在学习Python基础之控制流程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、掌握if语句之外的el...
阅读 2730·2021-11-24 09:39
阅读 1645·2021-09-28 09:35
阅读 1118·2021-09-06 15:02
阅读 1305·2021-07-25 21:37
阅读 2726·2019-08-30 15:53
阅读 3642·2019-08-30 14:07
阅读 713·2019-08-30 11:07
阅读 3511·2019-08-29 18:36