Every order passes through the same three steps: validate, charge, print. Maya wants to time that path without editing it. Where does the wrapper go?
A decorator is a function that takes another function and returns a replacement. The @ syntax is a compact way to apply that replacement at definition time. Wrappers are useful for logging, timing, validation, and access checks, but they should preserve the original function's metadata. You will also build a decorator factory, forward arguments, and reason about stacking order.
Functions can be passed around
A function is an object. You can store it in a variable, pass it to another function, and return it. A decorator uses exactly this ability: it receives the original function and builds a new callable around it.
def greet():
return "hello"
def call(func):
return func()
print(call(greet))A function as an argument
Wrap a function with @
The wrapper closes over func and calls it when the wrapped function is called. Placing @announce above greet is equivalent to writing greet = announce(greet) after the definition.
def announce(func):
def wrapper():
return "Before: " + func()
return wrapper
@announce
def greet():
return "hello"
print(greet())Decorator syntax
Preserve the original name
A wrapper replaces the original function object, so its __name__ would normally become wrapper. functools.wraps copies the important metadata back. Use it on every decorator you intend to keep.
from functools import wraps
def announce(func):
@wraps(func)
def wrapper():
return func().upper()
return wrapper
@announce
def greet():
return "hello"
print(greet())
print(greet.__name__)Keep the function identity
A decorator factory returns a decorator
When a decorator needs configuration, add an outer function. repeat(3) returns the actual decorator, which then receives the function. The three layers are easy to confuse: use the factory only when the behavior needs a parameter.
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
return [func(*args, **kwargs) for _ in range(times)]
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"hello {name}"
print(greet("Ada"))Configuration outside, function inside
Forward arguments and return values
A wrapper should accept *args and **kwargs and return the original result. Otherwise the decorator silently changes the function's interface. Use functools.wraps as well, so debugging and documentation still see the original name.
def trace(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(func.__name__, args, kwargs, "->", result)
return result
return wrapper
@trace
def add(a, b=0):
return a + b
print(add(2, b=3))Keep the signature flexible and the result intact
Stacking order is bottom-up
Decorators closest to def are applied first. @bold above @upper means upper wraps the function, then bold wraps that result. Read the stack from the bottom up when predicting output, even though it is written top-down.
def bold(func):
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def upper(func):
def wrapper():
return func().upper()
return wrapper
@bold
@upper
def title():
return "menu"
print(title())Bottom decorator runs first
Key points
- A decorator receives a function and returns a callable.
- @deco above def f is equivalent to f = deco(f).
- The inner wrapper runs when the decorated function is called.
- functools.wraps preserves the original function metadata.
- Factories return decorators; wrappers forward arguments; stacks apply bottom-up.
Common mistakes
- Forgetting to return the wrapper from the decorator.
- Calling the original function while defining the wrapper instead of inside it.
- Omitting functools.wraps and losing the original name and docstring.
- Forgetting the extra layer when a decorator needs an argument.
Write a decorator that adds square brackets around a function's string result.
Try it yourself
Change the values, predict the output, then run the code. The Python runtime is downloaded the first time you press Run, so the first run can take a few seconds.
Practice this lesson
12 questions with immediate explanations.