装饰器执行顺序


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

输出: "<b><i>hello world</i></b>"

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

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

这是否意味着对于装饰功能,它将首先运行该功能,然后移至其他装饰器的顶部?像@make_italic先那么@make_bold,而不是相反。

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


4
是的,它是从下而上开始的,并将结果传递给下一个
Padraic Cunningham 2014年

1
@PadraicCunningham评论也是答案的重要部分。有一个相关的问题(stackoverflow.com/questions/47042196/...
shookees

我会说它仍然是自顶向下的,从某种意义上说a(b(x))是自上而下的(如果您想像一下会分成3行)
joel

Answers:


126

装饰器包装正在装饰的功能。因此make_bold装饰了make_italic装饰器的结果,hello功能。

@decorator语法真的只是语法糖; 下列:

@decorator
def decorated_function():
    # ...

实际执行为:

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

用返回的内容替换原始decorated_function对象decorator()

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

因此,您的示例:

@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(),它也是一个调用原始函数的lambda hello()。扩展所有这些电话,您将获得:

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

因此输出变为:

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

我明白。但这是否意味着在这种情况下有2个包装器时,IDE将自动检测并包装第一个包装器的结果?因为我以为 @make_bold #make_bold = make_bold(hello) @make_italic #make_italic = make_italic (hello)?我不确定是否基于此将包装第一个结果。还是对于这种2个包装的情况,IDE将按make_bold(make_italic(hello))您提到的那样使用,而不是我共享的包装?
2014年

3
@Newbie:您的IDE在这里什么也不做;是由Python进行包装。我在最后一个示例中展示了您的示例,make_bold()该示例包装了的输出make_italic(),该输出用于包装hello,因此等效于make_bold(make_italic(hello))
马丁·彼得斯

您可以不使用lambda来提供此代码的版本吗?我已经尝试过.format,但是不起作用。为何在此示例中使用lambda?我试图理解lambda及其在本示例中的工作方式,但仍然有问题。我知道lambda就像一行函数,与def函数的规范相比,可以很容易地传递?
新手2014年

def inner: return "<b>" + fn() + "</b>",则为return inner“常规”功能版本;没有太大的区别。
马丁·皮特斯

我总是对订单感到困惑。“ ...装饰器将从最接近“ def”语句的装饰器开始应用”, 我称之为“由内而外”。我认为马丁(Martijn)将此称为“外部”。这意味着make_italic 装饰器make_bold 装饰器之前执行,因为make_italic它最接近装饰器def。但是,我忘记了修饰代码的执行顺序:首先执行make_bold 修饰的(即,粗体lambda),然后执行make_italic 修饰的 lambda(即,斜体lambda)。
红豌豆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.