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?

Decorators wrap the function they are decorating. So make_bold decorated the result of the make_italic decorator, which decorated the hello function.

The @decorator syntax is really just syntactic sugar; the following:

    @decorator
    def decorated_function():
        # ...

is really executed as:

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

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

Stacking decorators repeats that process outward.

So your sample:

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

can be expanded to:

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

When you call hello() now, you are calling the object returned by make_bold(), really. make_bold() returned a lambda that calls the function make_bold wrapped, which is the return value of make_italic(), which is also a lambda that calls the original hello(). Expanding all these calls you get:

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

so the output becomes:

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

From: stackoverflow.com/q/27342149

Back to homepage or read more recommendations: