最近看到一个关于Flask的CTF(RealWorld CTF 2018 web题bookhub)文章
其中的一个trick是装饰器的顺序问题,就想写篇博客回顾下装饰器~

首先强烈推荐很久之前看的一篇博文
(翻译)理解PYTHON中的装饰器
关于什么是装饰器看这篇文章就好了~
这里主要想写关于多个装饰器的执行流程

装饰顺序

示例代码

# import pdb;pdb.set_trace()def functionOne(function_to_decorate):print("functionOne初始化")def wrapperOne():passreturn wrapperOnedef functionTwo(function_to_decorate):print("functionTwo初始化")def wrapperTwo():passreturn 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 wrapperOnedef 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 wrapperOnedef 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 Flaskapp = 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中,有这段注释:

Basically this example::@app.route('/')def index():passIs equivalent to the following::def index():passapp.add_url_rule('/', 'index', index)

转载于:https://www.cnblogs.com/bay1/p/10979285.html

Python装饰器-装饰流程,执行顺序相关推荐

  1. python装饰器调用顺序_聊一聊Python装饰器的代码执行顺序

    为什么写这篇文章? 起因是QQ群里边有人提了一个问题:之前导入模块只需要1~2秒,为什么现在变成需要2~3分钟? 我的第一感觉是:是不是导入的模块顶层代码里边,做了什么耗时的事情.隔了一天,他的问题解 ...

  2. python多个for的执行顺序-python_装饰器篇(多个装饰器下的执行顺序)

    在之前的帖子中,简单自我总结了装饰器的几个情况以及基本上使用,那么有基本上说的都是单个的装饰器修饰方法 有时候我们会发现一个方法上面有多个装饰器 如下: @dec2 @dec def a(a): if ...

  3. Python(IT峰)笔记12-装饰器概念,装饰器的原型,装饰器的嵌套,装饰带有参数的函数,装饰器的嵌套,装饰带有多参数的函数,带有参数的装饰器,用类方法装饰函数,用韩式装饰器装饰类,用类装饰器装饰类

    1.装饰器decorator概念 在不改变原有函数代码,且保持原函数调用方法的基础上,给原函数增加新的功能(给类增加属性或方法) 用一个函数或类去装饰一个旧函数(或类)造出一个新函数(或新类) 在原有 ...

  4. python装饰器类-Python 装饰器装饰类中的方法

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...

  5. python装饰器实例-基于Python 装饰器装饰类中的方法实例

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...

  6. python有参装饰器 多个装饰器装饰一个

    1.有参装饰器 基本版 def auth(argv):def wrapper(f):def inner(*args,**kwargs):f(*args,**kwargs)return innerret ...

  7. python 装饰器装饰类_5分钟的Python装饰器指南

    python 装饰器装饰类 重点 (Top highlight) There's no doubt that Python decorators are one of the more advance ...

  8. python装饰器特性iy雾_扣丁学堂简述Python 装饰器装饰类中的方法

    扣丁学堂简述 Python 装饰器装饰类中的方法 本篇文章小编主要和读者们分享一下 Python 装饰器装饰类中的方法, 文中会有详细的代码 列出供大家参考,下面随小编一起来了解一下吧. 有一个类 T ...

  9. 51 Python - 装饰器 参数化装饰器——装饰器更通用

    05参数化装饰器--装饰器更通用 参数化装饰器如何理解,简单理解就是让装饰器可以通用.场景举例,现在有个需求要改某一段文字,既要加<P>标签,又要加<B>,还有加<Div ...

  10. python 装饰器装饰类

    当用装饰器装饰类的时候,就是对类进行操作 https://www.jb51.net/article/168276.htm

最新文章

  1. golang语言-1-go普及知识
  2. 蓝桥杯 历届试题 分考场(DFS+枚举)
  3. go mysql存储过程_Golang 调用MySQL存储过程
  4. python 全中文匹配字符_Python教程:进程和线程amp;正则表达式
  5. 五年来,开源论坛软件MyBB共修复100多个漏洞
  6. python除数为0报错_python 错误捕获机制分析
  7. qq群管机器人php,常用几款QQ群管机器人软件功能和体验对比
  8. python无法打开_终端里为什么无法运行python?
  9. 20211218:口罩数据汇总
  10. stderr/stdout用法
  11. 如何使用卷积神经网络进行图像处理?
  12. Lua中如何判读number是偶数还是奇数
  13. 刀片服务器性能对比,刀片服务器对比-刀锋上的较量
  14. WEB安全基础简单总结(有些无序,大佬勿喷)
  15. java毕业论文云笔记_开题报告基于Java云笔记管理系统.doc
  16. 为什么要使用零知识证明来开发跨链协议
  17. 【书籍篇】统计数字会说谎
  18. python pppoe拨号_批处理版普通宽带连接ADSL(PPPOE)创建工具
  19. Python从C盘到D盘
  20. torch学习 (三十七):DCGAN详解

热门文章

  1. 诺奖10年,干细胞领域再突破!华大单细胞技术助力获得人类体外诱导全能干细胞...
  2. AI 如果 “智力爆炸” ,只有普通智力的人类是蝼蚁还是宠物?
  3. 美军重视扩展现实技术的研究和应用
  4. 印度首次挑战登月告败,一步之遥≈多大差距?
  5. 机器人大潮中暗藏多少伪命题
  6. 人工智能的恶意用途:预测、预防和缓解
  7. Google智能生态链的演进路径
  8. 联合国《2017年信息经济报告》
  9. 在内存只有 24KB 的电脑上写操作系统,是怎样的体验?
  10. 刷爆技术圈的《知识图谱》终于补货了,最后 968 份,低至 2 折,抢完不补!...