Python Decorators Explained: @ Syntax and functools.wraps
A Python decorator takes a function and returns a replacement. Use @ syntax to apply it when defining a function, often adding behavior around each call.
Read First-class functions and closures first.
Wrap a function
The decorator creates a wrapper that calls the original function. Return the wrapper without calling it.
def announce(function):
def wrapper():
print("Starting")
return function()
return wrapper
def greet():
return "Hello"
greet = announce(greet)
print(greet())
Calling the replacement prints Starting, then returns Hello for the outer print().
Apply a decorator with @ syntax
Place @announce directly above the function definition. It has the same effect as greet = announce(greet).
def announce(function):
def wrapper():
print("Starting")
return function()
return wrapper
@announce
def greet():
return "Hello"
print(greet())
The output is again Starting, then Hello. Decoration happens at definition time; the wrapper runs when called.
Forward arguments and preserve metadata with wraps
*args collects positional arguments; **kwargs collects keyword arguments. Pass both on and return the original result.
from functools import wraps
def announce(function):
@wraps(function)
def wrapper(*args, **kwargs):
print("Starting")
return function(*args, **kwargs)
return wrapper
@announce
def greet(name, punctuation="!"):
"""Build a greeting."""
return f"Hello, {name}{punctuation}"
print(greet("Ada", punctuation="?"))
print(greet.__name__)
print(greet.__doc__)
wraps() preserves metadata such as the original name and docstring. Without it, the name would be wrapper.
See also: Parameters and return, First-class functions and closures.
Your turn
Apply @double_result to points, then print the result of calling points().
from functools import wraps
def double_result(function):
@wraps(function)
def wrapper(*args, **kwargs):
return function(*args, **kwargs) * 2
return wrapper
# add one line here: decorate points with double_result
def points():
return 7
# add one line here: print the result of calling points()
Expected output
14