【问题标题】:Decorator execution order装饰器执行顺序
【发布时间】:2015-02-05 04:09:28
【问题描述】:
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()

输出:"&lt;b&gt;&lt;i&gt;hello world&lt;/i&gt;&lt;/b&gt;"

我大致了解装饰器以及在大多数示例中它如何与其中之一一起使用。

在这个例子中,有 2 个。从输出看,似乎@make_italic先执行,然后@make_bold

这是否意味着对于装饰函数,它会先运行该函数,然后再向顶部移动其他装饰器?比如@make_italic,然后是@make_bold,而不是相反。

所以这意味着它不同于大多数编程语言中自顶向下方法的规范?只是为了这个装饰器?还是我错了?

【问题讨论】:

  • 是的,它从下往上开始,将结果传递给下一个
  • @PadraicCunningham 评论也是答案的重要部分。有相关问题 (stackoverflow.com/questions/47042196/…)
  • 我会说它仍然是自上而下的,因为a(b(x)) 是自上而下的(如果您想象分成 3 行)

标签: python decorator python-decorators


【解决方案1】:

装饰器包装他们正在装饰的功能。所以make_bold 修饰了make_italic 修饰器的结果,它修饰了hello 函数。

@decorator 语法实际上只是语法糖;以下:

@decorator
def decorated_function():
    # ...

实际上是这样执行的:

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

用返回的decorator() 替换原来的decorated_function 对象。

堆叠装饰器重复该过程向外

所以你的样本:

@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()返回了一个lambda,它调用了函数make_bold被包裹,这是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>"

【讨论】:

  • 我明白了。但这是否意味着这种情况下当有 2 个 wrapper 时,IDE 会自动检测并包装第一个 wrapper 的结果?因为我以为@make_bold #make_bold = make_bold(hello)@make_italic #make_italic = make_italic (hello)?我不确定是否基于此,它会包装第一个结果。或者对于这种 2 个包装器的情况,IDE 将使用您提到的 make_bold(make_italic(hello)) 而不是我共享的内容?
  • @Newbie:您的 IDE 在这里什么都不做;是 Python 进行包装。我在上一个示例中向您展示了make_bold() 包装了make_italic() 的输出,该输出用于包装hello,因此相当于make_bold(make_italic(hello))
  • 你能提供一个不使用 lambda 的代码版本吗?我试过 .format 但不起作用。为什么在这个例子中使用 lambda?我试图了解 lambda 以及它在此示例中的工作原理,但仍然存在问题。我知道 lambda 就像一行函数,与 def 函数的规范相比可以更容易地传递?
  • def inner: return "&lt;b&gt;" + fn() + "&lt;/b&gt;",然后return inner 将是“常规”功能版本;差别不大。
  • 我总是对订单感到困惑。 "...decorators will be applied starting with the one closest to the "def" statement"我称之为“由内而外”。我认为 Martijn 将其称为“外向”。这意味着make_italic decoratormake_bold decorator 之前执行,因为make_italic 最接近def。但是,我忘记了 decorated 代码执行顺序:首先执行make_bold decorated(即粗体lambda),然后是make_italic decorated lambda(即斜体 lambda)。
猜你喜欢
  • 1970-01-01
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2012-01-20
  • 2016-07-25
相关资源
最近更新 更多