explore-flask-zh icon indicating copy to clipboard operation
explore-flask-zh copied to clipboard

多个装饰器执行顺序的理解

Open mirrornight opened this issue 6 years ago • 4 comments

mirrornight avatar Jul 25 '19 06:07 mirrornight

@mirrornight 你跑下这段代码,看下输出?

#!/usr/bin/env python3
# coding: utf-8
import functools

def foo(func):
    @functools.wraps(func)
    def foo(*args, **kwargs):
        print("foo")
        func(*args, **kwargs)
    return foo

def bar(func):
    @functools.wraps(func)
    def bar(*args, **kwargs):
        print("bar")
        func(*args, **kwargs)
    return bar

@foo
@bar
def one():
    raise Exception

r1 = one()

spacewander avatar Jul 25 '19 11:07 spacewander

上面的代码的输出是:

foo
bar
Traceback (most recent call last):
  File "./x.py", line 24, in <module>
    r1 = one()
  File "./x.py", line 9, in foo
    func(*args, **kwargs)
  File "./x.py", line 16, in bar
    func(*args, **kwargs)
  File "./x.py", line 22, in one
    raise Exception
Exception

装饰器是由顶而下执行的。

感谢你抽空提出 PR,但是这个修改并不是正确的。

spacewander avatar Jul 26 '19 03:07 spacewander

等等! import functools def foo(func): print('get in foo') @functools.wraps(func) def foo(*args, **kwargs): print("foo") func(*args, **kwargs) return foo

def bar(func): print('get in bar') @functools.wraps(func) def bar(*args, **kwargs): print("bar") func(*args, **kwargs) return bar

@foo @bar def one(): raise Exception

r1 = one()

输出: get in bar get in foo foo bar Traceback (most recent call last): ... raise Exception Exception

mirrornight avatar Jul 29 '19 13:07 mirrornight

@mirrornight 从语法上看,你是对的。这里面其实有两组会被执行的“装饰器”,一种是装饰器函数自己,另一种是装饰器函数返回的函数。比如在下面这个例子里,

import functools

def foo(func):
    print('get in foo')
    @functools.wraps(func)
    def _foo(*args, **kwargs):
        print("foo")
        func(*args, **kwargs)
    return _foo

def bar(func):
    print('get in bar')
    @functools.wraps(func)
    def _bar(*args, **kwargs):
        print("bar")
        func(*args, **kwargs)
    return _bar

@foo
@bar
def one():
    raise Exception

one()

foobar 会在解析 one 时执行,顺序是先 barfoo。而 _foo_bar 会在运行 one 时执行,顺序是先 _foo_bar

从上下文看,原作者描述的应该是类似于运行 one 的场景。不过这里的装饰器执行顺序的表达容易引起误会,也许应该改成“洋葱模型”这种表达?

spacewander avatar Jul 30 '19 04:07 spacewander