What is a decorator in Python, and how does it work under the hood?
A decorator is a callable that takes a function, wraps it with extra behaviour, and returns the new callable. The @syntax is syntactic sugar for reassigning the function name to the wrapper immediately after definition.
How to think about it
What the interviewer wants to hear
They want to know you can explain decorators without just saying “it adds functionality to a function.” The key things to hit: it’s a callable that takes a function and returns a callable, the @ syntax is syntactic sugar for name-rebinding, and functools.wraps is essential. Then show you can build one from scratch.
The mechanism in plain terms
The @decorator syntax is exactly equivalent to calling the decorator manually:
def greet(name):
print(f"Hello, {name}")
greet = decorator(greet) # @decorator does this automatically
Python evaluates decorator(greet) right after the def, and binds the name greet to whatever is returned. If the decorator returns a wrapper function, greet now points to that wrapper — which closes over the original function.
Building a timing decorator step by step
Stacking decorators
Stacking applies them bottom-up — the decorator closest to the def runs first:
@timer
@repeat(times=3)
def fetch_data(url): ...
# Equivalent to: fetch_data = timer(repeat(times=3)(fetch_data))
Key insight
A decorator is just a higher-order function — a function that takes a function and returns a function. @ is purely syntactic sugar. Once you internalise that, you can reason about any decorator, including class-based decorators and decorators that accept arguments.