Decorator execution order

def make_bold(fn):
return lambda : "<b>" + fn() + "</b>"


def make_italic(fn):
return lambda : "<i>" + fn() + "</i>"


@make_bold
@make_italic
def hello():
return "hello world"


helloHTML = hello()

Output: "<b><i>hello world</i></b>"

I roughly understand about decorators and how it works with one of it in most examples.

In this example, there are 2 of it. From the output, it seems that @make_italic executes first, then @make_bold.

Does this mean that for decorated functions, it will first run the function first then moving towards to the top for other decorators? Like @make_italic first then @make_bold, instead of the opposite.

So this means that it is different from the norm of top-down approach in most programming lang? Just for this case of decorator? Or am I wrong?

55200 次浏览

装饰者 包装的装饰功能。因此,make_bold修饰了 make_italic修饰符的结果,make_italic修饰符修饰了 hello函数。

@decorator语法实际上只是一种语法糖,如下所示:

@decorator
def decorated_function():
# ...

真正的执行方式是:

def decorated_function():
# ...
decorated_function = decorator(decorated_function)

replacing the original decorated_function object with whatever decorator() returned.

堆叠装饰器重复这个过程 向外

那么你的例子是:

@make_bold
@make_italic
def hello():
return "hello world"

可扩展至:

def hello():
return "hello world"
hello = make_bold(make_italic(hello))

现在调用 hello()时,实际上是在调用由 make_bold()返回的对象。make_bold()返回一个调用函数 make_boldlambda,它是 make_italic()的返回值,也是一个调用原 hello()的 lambda。扩大所有这些电话,你会得到:

hello() = lambda : "<b>" + fn() + "</b>" #  where fn() ->
lambda : "<i>" + fn() + "</i>" # where fn() ->
return "hello world"

所以输出变成:

"<b>" + ("<i>" + ("hello world") + "</i>") + "</b>"

我认为这个问题的答案似乎很简单,但事实并非如此。当我们讨论修饰符的执行顺序时,我认为我们必须记住修饰符本身在执行过程中的不同时刻被计算: 当 Python 解释器计算修饰方法定义本身时,以及当修饰方法被调用/执行时。我在实验中看到的修饰器的顺序在这两个阶段之间是不同的。

此外,请记住,当修饰一个函数时,我们可以有执行 之前的代码,修饰的方法和运行 之后的代码。这使得嵌套装饰者的工作变得更加复杂。

简而言之:

  • 当解释器计算修饰方法定义时,修饰符从 < strong > bottom-> top 计算
  • 当解释器调用修饰方法时,修饰符将从 < strong > top-> bottom 中调用。

考虑下面的代码示例:

print("========== Definition ==========")
def decorator(extra):
print(" in decorator factory for %s " % extra)
extra = " %s" % extra
def inner(func):
print(" defining decorator %s " % extra)
def wrapper(*args, **kwargs):
print("before %s -- %s" % (func.__name__, extra))
func(*args, **kwargs)
print("after %s -- %s" % (func.__name__, extra))
return wrapper
return inner


@decorator('first')
@decorator('middle')
@decorator('last')
def hello():
print('  Hello ')


print("\n========== Execution ==========")
hello()

该代码的输出如下:

========== Definition ==========
in decorator factory for first
in decorator factory for middle
in decorator factory for last
defining decorator  last
defining decorator  middle
defining decorator  first


========== Execution ==========
before wrapper --  first
before wrapper --  middle
before hello --  last
Hello
after hello --  last
after wrapper --  middle
after wrapper --  first

正如我们可以在这个输出中看到的顺序是不同的(如前所述)。在定义过程中,从底部到顶部对修饰符进行评估,同时在执行过程中(这是一般情况下最重要的部分)从顶部到底部对修饰符进行评估。

回到问题中提出的示例,下面是一个示例代码(不使用 lambda) :

print("========== Definition ==========")
def make_bold(fn):
print("make_bold decorator")
def wrapper():
print("bold")
return "<b>" + fn() + "</b>"
return wrapper


def make_italic(fn):
print("make_italic decorator")
def wrapper():
print("italic")
return "<i>" + fn() + "</i>"
return wrapper


@make_bold
@make_italic
def hello():
return "hello world"


print("\n========== Execution ==========")
print(hello())

在这种情况下的输出:

========== Definition ==========
make_italic decorator
make_bold decorator


========== Execution ==========
bold
italic
<b><i>hello world</i></b>

新的执行顺序是从上到下的。我们可以将同样的方法应用于原始代码(稍作修改以打印我们在哪里) :

print("========== Definition ==========")


def make_bold(fn):
print("make_bold")
return lambda: print("exec_bold") or "<b>" + fn() + "</b>"


def make_italic(fn):
print("make_italic")
return lambda: print("exec_italic") or "<i>" + fn() + "</i>"


@make_bold
@make_italic
def hello():
return "hello world"


print("\n========== Execution ==========")
print(hello())

输出结果是:

========== Definition ==========
make_italic
make_bold


========== Execution ==========
exec_bold
exec_italic
<b><i>hello world</i></b>

我希望本文能够对 Python 中的装饰器顺序以及如何处理它提供一些帮助。