Skip to content

Instantly share code, notes, and snippets.

@kirsle
Created September 22, 2014 22:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kirsle/bf24622fc5f255256c6e to your computer and use it in GitHub Desktop.
Save kirsle/bf24622fc5f255256c6e to your computer and use it in GitHub Desktop.
Python decorator order

What order does Python execute decorators in? It seems like this changed sometime between when they were first introduced (2.4) and now (2.7).

Given the code:

@foo
@bar
@baz
def myfunc():
    pass

Is the decorator calling order baz -> bar -> foo, or is it foo -> bar -> baz? It used to be from the bottom to the top (the decorator closest to your function def runs first), but now it seems to be run from top to bottom.

Or rather, is the code equivalent to foo(bar(baz(myfunc))) (bottom to top) or baz(bar(foo(myfunc))) (top to bottom)

Links that support one vs. the other:

Test script:

#!/usr/bin/env python

from functools import wraps

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        print "login_required called!"
        return f(*args, **kwargs)
    return decorated_function


def ajax_request(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        print "ajax_request called!"
        return f(*args, **kwargs)
    return decorated_function


@login_required
@ajax_request
def test1():
    print "Test 1, login above ajax"


@ajax_request
@login_required
def test2():
    print "Test 2, ajax above login"


test1()
print "=" * 80
test2()

Output (Python 2.7.5):

** login_required called!
** ajax_request called!
Test 1, login above ajax
================================================================================
** ajax_request called!
** login_required called!
Test 2, ajax above login
@Veedrac
Copy link

Veedrac commented Sep 23, 2014

@kirsle
Copy link
Author

kirsle commented Dec 11, 2014

That issue implies that the decorators would execute from bottom to top, but my test script on Python 2.7.5 executed them from top to bottom.

@login_required
@ajax_request
def test1():
    print "Test 1, login above ajax"
** login_required called!
** ajax_request called!
Test 1, login above ajax

Unless there's something wrong with my test script.

@prashantsengar
Copy link

If someone is still looking at it, the decorators are executed from top to bottom. You can read the docs or check it by trial.

@Lewiscowles1986
Copy link

Lewiscowles1986 commented Jul 24, 2021

Here is a code example that helps to remember

def decorator(extra):
    extra = f" {extra}"
    def inner(func):
        def wrapper(*args, **kwargs):
            print(f"before {func.__name__}{extra}")
            func(*args, **kwargs)
            print(f"after {func.__name__}{extra}")
        return wrapper
    return inner

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

hello()

This outputs

before wrapper first
before wrapper middle
before hello last
Hello
after hello last
after wrapper middle
after wrapper first

I feel like the before and after of the onion skinning is what mostly confuses people.
But also, notice the definition of hello is only passed to the first in the onion skin. What is passed to the second is the wrapped hello output of the @decorator('last'). Don't confuse execution order with definition order.

decorator("first")(decorator("second")(decorator("third")(hello)))()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment