python装饰器执行顺序_Python装饰器-装饰流程,执行顺序

最近看到一个关于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

这个执行顺序可能也困扰了很多人,现在我们从输出结果看

对照代码,就很容易清楚了,执行到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)()

-> @loginRequire

(Pdb) s

> c:\users\bayi\desktop\test\256.py(18)()

-> @app.route('/up')

(Pdb) s

> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1252)route()->

-> return decorator

(Pdb) s

--Call--

> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.py(1248)decorator()

-> def decorator(f):

(Pdb) f

(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

#===================================================================================#

上方up 下方down

#===================================================================================#

> c:\users\bayi\desktop\test\256.py(22)()

-> @app.route('/down')

(Pdb) s

> c:\users\bayi\desktop\test\256.py(23)()

-> @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()->

-> return wrapperTwo

(Pdb) s

--Call--

> c:\users\bayi\.virtualenvs\test-gq7eoxbq\lib\site-packages\flask\app.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:\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

也就是添加路由的时候会选择丢失外层的路由,只装饰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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值