Python装饰器-装饰流程,执行顺序
最近看到一个关于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装饰器-装饰流程,执行顺序相关推荐
- python装饰器调用顺序_聊一聊Python装饰器的代码执行顺序
为什么写这篇文章? 起因是QQ群里边有人提了一个问题:之前导入模块只需要1~2秒,为什么现在变成需要2~3分钟? 我的第一感觉是:是不是导入的模块顶层代码里边,做了什么耗时的事情.隔了一天,他的问题解 ...
- python多个for的执行顺序-python_装饰器篇(多个装饰器下的执行顺序)
在之前的帖子中,简单自我总结了装饰器的几个情况以及基本上使用,那么有基本上说的都是单个的装饰器修饰方法 有时候我们会发现一个方法上面有多个装饰器 如下: @dec2 @dec def a(a): if ...
- Python(IT峰)笔记12-装饰器概念,装饰器的原型,装饰器的嵌套,装饰带有参数的函数,装饰器的嵌套,装饰带有多参数的函数,带有参数的装饰器,用类方法装饰函数,用韩式装饰器装饰类,用类装饰器装饰类
1.装饰器decorator概念 在不改变原有函数代码,且保持原函数调用方法的基础上,给原函数增加新的功能(给类增加属性或方法) 用一个函数或类去装饰一个旧函数(或类)造出一个新函数(或新类) 在原有 ...
- python装饰器类-Python 装饰器装饰类中的方法
title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...
- python装饰器实例-基于Python 装饰器装饰类中的方法实例
title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] categ ...
- python有参装饰器 多个装饰器装饰一个
1.有参装饰器 基本版 def auth(argv):def wrapper(f):def inner(*args,**kwargs):f(*args,**kwargs)return innerret ...
- python 装饰器装饰类_5分钟的Python装饰器指南
python 装饰器装饰类 重点 (Top highlight) There's no doubt that Python decorators are one of the more advance ...
- python装饰器特性iy雾_扣丁学堂简述Python 装饰器装饰类中的方法
扣丁学堂简述 Python 装饰器装饰类中的方法 本篇文章小编主要和读者们分享一下 Python 装饰器装饰类中的方法, 文中会有详细的代码 列出供大家参考,下面随小编一起来了解一下吧. 有一个类 T ...
- 51 Python - 装饰器 参数化装饰器——装饰器更通用
05参数化装饰器--装饰器更通用 参数化装饰器如何理解,简单理解就是让装饰器可以通用.场景举例,现在有个需求要改某一段文字,既要加<P>标签,又要加<B>,还有加<Div ...
- python 装饰器装饰类
当用装饰器装饰类的时候,就是对类进行操作 https://www.jb51.net/article/168276.htm
最新文章
- golang语言-1-go普及知识
- 蓝桥杯 历届试题 分考场(DFS+枚举)
- go mysql存储过程_Golang 调用MySQL存储过程
- python 全中文匹配字符_Python教程:进程和线程amp;正则表达式
- 五年来,开源论坛软件MyBB共修复100多个漏洞
- python除数为0报错_python 错误捕获机制分析
- qq群管机器人php,常用几款QQ群管机器人软件功能和体验对比
- python无法打开_终端里为什么无法运行python?
- 20211218:口罩数据汇总
- stderr/stdout用法
- 如何使用卷积神经网络进行图像处理?
- Lua中如何判读number是偶数还是奇数
- 刀片服务器性能对比,刀片服务器对比-刀锋上的较量
- WEB安全基础简单总结(有些无序,大佬勿喷)
- java毕业论文云笔记_开题报告基于Java云笔记管理系统.doc
- 为什么要使用零知识证明来开发跨链协议
- 【书籍篇】统计数字会说谎
- python pppoe拨号_批处理版普通宽带连接ADSL(PPPOE)创建工具
- Python从C盘到D盘
- torch学习 (三十七):DCGAN详解
热门文章
- 诺奖10年,干细胞领域再突破!华大单细胞技术助力获得人类体外诱导全能干细胞...
- AI 如果 “智力爆炸” ,只有普通智力的人类是蝼蚁还是宠物?
- 美军重视扩展现实技术的研究和应用
- 印度首次挑战登月告败,一步之遥≈多大差距?
- 机器人大潮中暗藏多少伪命题
- 人工智能的恶意用途:预测、预防和缓解
- Google智能生态链的演进路径
- 联合国《2017年信息经济报告》
- 在内存只有 24KB 的电脑上写操作系统,是怎样的体验?
- 刷爆技术圈的《知识图谱》终于补货了,最后 968 份,低至 2 折,抢完不补!...