Goal
You’ll understand what a decorator actually does under the hood, not just how to apply the @ syntax, since understanding the mechanism is what makes decorators genuinely useful rather than confusing magic.
Learn
A decorator wraps a function with additional behavior, without modifying the original function’s own code. The @ syntax is shorthand for a specific, understandable pattern:
def my_decorator(func):
def wrapper():
print("Before the function runs")
func()
print("After the function runs")
return wrapper
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello) # manually applying the decorator
say_hello()
# "Before the function runs"
# "Hello!"
# "After the function runs" The @ syntax is exactly equivalent shorthand for that manual reassignment shown above:
@my_decorator
def say_hello():
print("Hello!")
say_hello() # identical result to the manual version above Understanding this mechanism matters: my_decorator is just a regular function that takes another function as an argument, and returns a new function (wrapper) that adds behavior around the original. The @ syntax doesn’t do anything magical or fundamentally different — it’s purely a more readable way to write say_hello = my_decorator(say_hello).
Real, common uses for decorators include logging when a function is called, timing how long a function takes to run, or checking permissions before allowing a function to execute — any case where you want to add consistent behavior around multiple different functions, without duplicating that behavior’s code inside each one individually.
Decision Task
You see @my_decorator above a function definition for the first time, with no prior explanation. Before reading on: based purely on this lesson’s explanation, what does this syntax actually do, in plain terms — not just “it decorates the function”?
Show Answer
It’s exact shorthand for function_name = my_decorator(function_name) — it reassigns the function name to point to whatever new function my_decorator returns (typically a wrapper function that adds behavior around the original), rather than doing anything fundamentally different from a normal function call and reassignment.
Common Mistake
Treating decorators as unexplainable “magic syntax” to copy-paste without understanding the underlying mechanism — that a decorator is just a regular function taking another function as its argument and returning a new one. This misunderstanding makes it genuinely hard to write your own decorators or debug unexpected behavior, since the actual mechanism (a plain function call and reassignment) is straightforward once explicitly understood.
Practice Questions
1. What is the @ syntax before a function definition exact shorthand for?
Show Answer
function_name = decorator_name(function_name) — reassigning the function to whatever the decorator returns.
2. In the my_decorator example, what does the wrapper function actually do?
Show Answer
It runs some code before calling the original function, calls the original function itself, then runs some code after — adding behavior around the original without modifying its own code.
3. Is a decorator itself a special kind of object, or just a regular Python function?
Show Answer
Just a regular Python function — one that specifically takes another function as an argument and returns a new function.
4. True or False: using @ decorator syntax does something Python can’t achieve through a normal function call.
Show Answer
False — it’s purely more readable shorthand for a normal function call and reassignment; nothing about it is fundamentally different or impossible without the @ syntax.
5. Name one genuinely common real-world use case for decorators mentioned in this lesson.
Show Answer
Logging when a function is called, timing how long it takes to run, or checking permissions before execution (any one of these).
Try It Yourself
Without looking back, write out (in plain code, not necessarily runnable) what @my_decorator above a function definition is exact shorthand for, using the function name process_data as your example.
Show Answer
process_data = my_decorator(process_data) — this is the exact, literal equivalent of writing @my_decorator directly above the process_data function definition.
Quick Check
1. What does a decorator do to a function?
Show Answer
Wraps it with additional behavior, without modifying the original function’s own code.
2. What is the @ syntax exact shorthand for?
Show Answer
function_name = decorator_name(function_name).
3. Is a decorator a special Python construct, or just a regular function?
Show Answer
A regular function that takes another function as an argument and returns a new one.
4. What does the wrapper function inside a typical decorator do?
Show Answer
Adds behavior before and/or after calling the original wrapped function.
5. Name one common real use case for decorators.
Show Answer
Logging, timing, or permission-checking (any reasonable example).