skip to content

When `@a` is stacked above `@b` on a Python function, which decorator wraps the function first and which one runs first at call time?

level: juniorimportance: must knowfreq 68%

answer

  1. Two timelines, not one
  2. One order at import, another at call
  3. Nearest the def is applied first
  4. Desugars to a(b(f))
  5. Outermost wrapper enters first

basics

~10 s

Decorators apply bottom-up: @b wraps the function first, then @a wraps that result, exactly like writing f = a(b(f)). At call time the outermost wrapper runs first, so @a's code executes before @b's.

solid answer

~40 s

A stacked function has two separate orders. **Application** happens bottom-up at decoration time: the decorator nearest the `def` receives the original function object, and each one above it receives whatever the one below returned. `@a` over `@b` over `def f` is precisely `f = a(b(f))`. **Execution** then runs top-down: the name `f` is now bound to `a`'s wrapper, so calling it enters `a`'s wrapper first, which calls `b`'s wrapper, which calls the original body; the return value unwinds in the opposite direction, `b` first, then `a`. So the topmost decorator is applied last but sees the call first and the return value last. The decorator functions themselves run once, when the `def` statement executes at import; only the wrapper bodies run per call.

code

python · 27 lines
python
def a(f):
    print("apply a")
    def wrapper(*args, **kwargs):
        print("enter a")
        result = f(*args, **kwargs)
        print("exit a")
        return result
    return wrapper


def b(f):
    print("apply b")
    def wrapper(*args, **kwargs):
        print("enter b")
        result = f(*args, **kwargs)
        print("exit b")
        return result
    return wrapper


@a
@b
def greet():
    print("body")


greet()

go deeper

for a junior

Be ready to rewrite @a @b def f as f = a(b(f)) on the spot and to state both orders in one sentence: applied bottom-up, executed top-down.

for a middle

Explain the mechanics — the name is bound to the outermost wrapper, each wrapper closes over the layer beneath, and the decorator bodies themselves run once at import.

for a senior

Show that you read a stack for behaviour: which layer can short-circuit, which sees exceptions, and which observes the raw return value before anything above it transforms it.

for a principal

Own the convention. Decide the house ordering for cross-cutting wrappers so reviewers never have to re-derive it, and argue when a stack has grown deep enough to be replaced by an explicit pipeline.

### Two timelines, not one Every decorated function lives on two timelines, and stacked decorators travel them in opposite directions. That inversion is the entire question, and it is why the topic is a whiteboard favourite. The first timeline is **decoration time**: the single moment when the `def` statement finishes executing, normally at import. The second is **call time**: every later invocation of the resulting object. ### Application is bottom-up The decorator syntax is pure sugar. This: ```python @a @b def f(): ... ``` means exactly this: ```python def f(): ... f = a(b(f)) ``` Read the desugared line inside-out. `b` is called with the original function object and returns something. `a` is called with *that* return value — not with the original function — and whatever `a` returns is bound to the name `f`. So the decorator physically closest to the `def` line is applied first, and the stack is built from the bottom up, one layer at a time. Two consequences follow immediately. First, everything above `@b` sees `b`'s return value, not the original function; if `b` returns a plain wrapper function, `a` can wrap it like any callable, but if `b` returns some other kind of object, `a` must be able to cope with that object. Second, any side effect a decorator performs at decoration time — registering the function in a table, validating its signature, emitting a warning — happens in bottom-up order, once, at import. ### Execution is top-down After decoration, the name `f` is bound to the outermost object: `a`'s wrapper. Calling `f()` therefore enters `a`'s wrapper body first. That wrapper does its "before" work, then calls what it closed over — `b`'s wrapper — which does *its* before work and finally calls the original body. The return value then unwinds outward: the body returns to `b`, `b` returns to `a`, `a` returns to the caller. So the shape at call time is a set of nested parentheses: ``` enter a -> enter b -> body -> exit b -> exit a ``` Stated as a rule you can say out loud in an interview: **the topmost decorator is applied last, but it is the first to see the call and the last to see the result.** The bottommost is applied first, is closest to the body, and sees the call last. ### Why the two orders are not a contradiction They are the same order viewed from different ends. Building the stack goes inside-out; entering the stack goes outside-in. It is the ordinary behaviour of function composition: to compute `a(b(x))` you must evaluate `b` first, but the *call* to `a` is the one you wrote outermost and the one that returns to the caller. Decorators are composition over functions rather than over values, and `@` is the notation for it. ### Decoration runs once; wrappers run per call A common junior mistake is expecting `a` and `b` themselves to run on every call. They do not. Print statements placed directly in a decorator body fire once, at import, in bottom-up order; print statements inside the wrapper fire on every call, in top-down order. Distinguishing those two positions in a single snippet is the fastest way to prove you understand the model, and it is what the code example below shows. ### Reading a stack in code review Practical reading rules: - Top of the stack = outermost = the first thing that can reject, short-circuit, time, or log a call. - Bottom of the stack = innermost = the last thing before the real body, and the first to see the raw return value. - A decorator that may **return early** without delegating (a cache, a feature flag, a circuit breaker) hides everything beneath it for that call. - A decorator that must observe **every** call belongs above anything that can short-circuit. - Exceptions propagate outward in the same direction as return values, so a handler placed high in the stack sees exceptions raised by everything below it. ### The syntax itself The ordering semantics have never changed. Python 3.9 (PEP 614) relaxed the *grammar* so that any expression may follow `@` rather than only a dotted name with an optional call, but it did not touch application or execution order; CPython 3.14 behaves as described here. Stacked decorators on classes work identically — the class object is passed bottom-up — and there is no limit on stack depth beyond readability. ### Talking about it If asked to justify the design, the honest answer is that `@` was chosen to read as an annotation directly above the definition, and the nearest annotation is the one applied first because the sugar expands outward from the `def`. Once you can rewrite any stack as nested calls, no further memorisation is needed: read `a(b(c(f)))` and both orders fall out of it.

  • If you put a `print` directly in a decorator's body rather than in its wrapper, when does it fire?
    Once, when the `def` statement it decorates is executed — normally at import time — and in bottom-up order across a stack. The wrapper body is the only part that runs per call. That difference is the cleanest way to demonstrate that decoration is a one-time transformation and not a per-call hook.
  • In `@a @b def f`, what object does `a` actually receive?
    Whatever `b` returned, not the original function. If `b` returns a wrapper closure, `a` wraps that closure; the original body is reachable only through `b`'s closure. This is why a decorator that assumes it is receiving the raw function — inspecting its signature, or its defaults — can behave differently depending on where it sits in the stack.
  • Does an exception raised in the function body unwind through the stack in application order or call order?
    Outward, the same direction as the return value: the body's exception propagates to the innermost wrapper, then upward through each enclosing wrapper, and finally to the caller. A `try`/`except` in the topmost decorator therefore catches failures raised anywhere below it, including in the other wrappers themselves.

Nested dolls: you assemble them from the innermost outward, but anyone opening the set meets the outermost one first.

saying these in an interview costs you the question

  • Says decorators apply top-down, nearest the def last
  • Claims call order matches application order
  • Thinks the decorator function itself runs on every call
  • Cannot rewrite a stack as nested calls
  • Believes order never matters because both just wrap
  • Says the bottom decorator sees the call first

context