🎀

Decorator Internals — What @ Actually Does

A function that takes a function and returns a function — that's all

@my_decorator
def greet():
    return 'hello'

# Above is identical to:
greet = my_decorator(greet)

@ isn't magic. It's syntactic convenience for applying a function that takes a function and returns a new function.

Basic Structure

wrapper is a closure capturing the outer function's variable (func). *args, **kwargs ensures the wrapper accepts any signature.

Why functools.wraps

Without it, greet.__name__ becomes 'wrapper'. Original metadata (name, docstring, signature) lost. @functools.wraps(func) copies original metadata.

Decorators with Arguments

Triple nesting. repeat(3) returns decorator, decorator(say_hello) returns wrapper. FastAPI's @app.get('/path') uses this pattern.

Key Points

1

@decorator is syntactic sugar for func = decorator(func)

2

wrapper function captures original func as closure

3

functools.wraps(func) essential to preserve original metadata

4

Decorators with args use triple nesting: outer(args) → decorator(func) → wrapper(*args)

Use Cases

Logging — auto-record function call time/args/return value FastAPI — route registration with @app.get("/path")