Function decorators
coreintermediateA decorator is a function that takes a function and returns a replacement for it. @my_decorator above def process(): is exactly process = my_decorator(process), just written before the definition instead of after.
Think of it as
A decorator is a gift wrapper placed around a function. The original function still exists and still runs, but every call now goes through the wrapping first — the wrapper decides whether to add something before, after, or around the call, without the caller ever needing to know the wrapping is there.
What we're doing: Show that @my_decorator above process() is exactly equivalent to reassigning process = my_decorator(process) by hand.
- 1
- my_decorator takes one function and returns wrapper — a new function that will stand in for it.
- 9
- @my_decorator runs my_decorator(process) immediately and rebinds the name process to whatever it returns.
- 22
- process2 = my_decorator(process2) does by hand exactly what the @ syntax did automatically two lines above.
Before the function runs
Processing...
After the function runs
--- manual equivalent ---
Before the function runs
Processing...
After the function runsWhy this works: process() and process2() print identical output because @my_decorator and the manual process2 = my_decorator(process2) line do the exact same thing — call my_decorator with the original function and rebind the name to its return value. The @ syntax is purely a shorter way to write that one reassignment, never a different mechanism.
wrapper forgets to return the original function's result
Wrong
Better
What you see: No exception — add(2, 3) silently prints None instead of 5, because wrapper never returns anything, so it defaults to None like any function that falls off the end.
Why: Once a decorator replaces process with wrapper, wrapper's return value IS the decorated function's return value from the caller's point of view — there is no other path back. Calling func(*args, **kwargs) without returning it discards the real result and silently substitutes None.
- def process(): ... — the original function object is built
- my_decorator(process) — runs once, at def time, and returns wrapper
- process = wrapper — the name process now points at wrapper instead
@decorator syntax and its plain-assignment equivalent
Together
Remember: @my_decorator above def is exactly name = my_decorator(name) — decoration runs once at def time, and wrapper must forward every argument and the return value.
See also: decorators with arguments · functools wraps · stacked decorators · closures

