What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
What does the @ symbol do above a Python function? It applies a decorator to the function object created by the definition, then binds the decorator’s result to the function’s name. In the common “gift wrapper” pattern, that result is a new callable that adds behavior around the original. But decorators can return other callables—or even non-callable objects—so wrapping is a common technique, not the whole definition.
What a decorator does
Think of a function as a gift and a decorator as an extra layer that changes how the gift is presented or used. The analogy is useful as long as it does not suggest that every decorator edits a function in place or calls the original function.
Python’s language reference says, “A function definition may be wrapped by one or more decorator expressions.” In practical terms, Python creates the function object, evaluates the decorator expression, applies the resulting decorator to the function, and binds the returned object to the function’s name. The reference describes this syntax in Python 3.14’s “Compound statements”.
A useful mental model for a single decorator is:
function_name = decorator(function_name)
This is an equivalent assignment model, not a claim that Python literally rewrites the source or executes the definition line by line in this form. The official PEP 318 uses this equivalence to explain decorator syntax.
#1 Best Overall
How a wrapper adds behavior
A typical decorator returns a wrapper function. The wrapper can do work before or after calling the original function, forward its arguments, and return its result.
from functools import wraps
def announce(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Starting")
result = func(*args, **kwargs)
print("Finished")
return result
return wrapper
@announce
def greet(name):
return f"Hello, {name}!"
When Python executes the decorated definition, it passes the function object for greet to announce and binds the returned wrapper to the name greet. Later, calling greet("Ada") calls that wrapper. The wrapper prints “Starting,” calls the original function with the supplied argument, prints “Finished,” and returns the original result, "Hello, Ada!".
Rank #2
The same relationship can be shown without @ syntax:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →def greet(name):
return f"Hello, {name}!"
greet = announce(greet)
This second form demonstrates the effect of decorator syntax; it is not a recommendation to rewrite decorated definitions manually.
When decoration happens—and when wrapper code runs
These are separate stages. The decorator is applied when Python executes the function definition. Code inside a returned wrapper runs later, when the decorated name is called. In the example, announce runs during definition, while the two print statements run on each call to greet.
This distinction matters when a decorator performs setup or replaces a function: that work happens during definition, not automatically each time the decorated function is called. Call-time behavior belongs in the returned callable, if the decorator returns one.
Why functools.wraps belongs on wrappers
A plain wrapper has its own name and docstring, which can obscure the original function’s visible metadata. Apply @wraps(func) to the inner wrapper to preserve useful metadata from func. The standard-library documentation also describes the wrapped callable as accessible through __wrapped__. See the Python 3.14.8 functools documentation.
Free tools Windows power users keep installed
One-click scans. No signup required.
For wrappers that call the original function, return its result unless changing the return behavior is intentional. Omitting return result in the example would make the decorated function return None instead of the greeting.
Best Value
How stacked decorators are applied
With multiple decorators, the one closest to def is applied first. The decorator above it receives that result:
@outer
@inner
def work():
...
# Conceptually:
work = outer(inner(work))
So inner is applied to the original function first, then outer is applied to the result. When work is called later, the object returned by outer is the callable reached through that name (if it is callable). The order of application is defined by the syntax; what happens during a call depends on what each decorator returns.
What changes with a decorator factory
When a decorator needs configuration, the expression after @ can call a factory that produces the decorator:
@repeat(3)
def wave():
...
Here, repeat(3) runs first and returns a decorator. That returned decorator then receives the function object for wave. The integer 3 is passed to the factory; it is not passed directly to wave by the decorator syntax. PEP 318 discusses this pattern along with stacked decorators.
Quick Recap
Three decorator forms at a glance
| Form | What receives what | Result |
|---|---|---|
@decorate |
decorate receives the function object created by the definition. |
The decorated name refers to the object returned by decorate. |
@factory(options) |
The factory receives its options and returns a decorator; that decorator receives the function object. | The decorated name refers to the object returned by the decorator. |
@outer above @inner |
inner receives the function first; outer receives the result. |
Conceptually, the name is bound to outer(inner(function)). |
Common mistakes to avoid
- Assuming every decorator is a wrapper. A decorator may return a different callable or another kind of object; it need not call the original.
- Mixing up definition time and call time. Applying the decorator and running the wrapper body are distinct events.
- Reversing stacked order. The decorator nearest the definition is applied first.
- Dropping the original result. A wrapper that delegates should usually return the delegated call’s result.
- Forgetting metadata. Use
functools.wrapsfor ordinary function wrappers when retaining the original function’s metadata is useful.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

