Decorators
Functions that wrap other functions and add behavior to them, such as logging or permission checks. Written as @name.
Example
#import functools
def log_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}{args}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result!r}")
return result
return wrapper
@log_call
def award(name, exp):
"""Awards experience points."""
return f"{name}: +{exp} EXP"
award("Emma", 15)
print(award.__name__, "|", award.__doc__)Definition and usage
#A decorator is a function that takes another function and returns its extended version. Writing @log_call above a function definition is shorthand for award = log_call(award). The mechanism works because in Python, functions are ordinary objects: you can pass them as arguments and return them from other functions.
A typical decorator defines a wrapper function inside itself. It accepts any arguments through *args and **kwargs, does something before calling the original, calls it, does something after, and returns its result. This way you can add logging, timing, permission checks, or retrying, without changing the functions themselves.
The wrapper hides the original function’s name and docstring: without an extra step, award.__name__ would return "wrapper". The @functools.wraps(func) decorator applied to the wrapper copies this information, so it is worth using it every time. A decorator with its own parameters, e.g. @require_level(5), needs one more level: the outer function accepts the parameters and returns the actual decorator.
Several decorators above one function run from the bottom up: the one closest to the definition wraps it first. The standard library has many ready-made decorators, for example @property, @classmethod, @dataclass, or @functools.lru_cache.
Syntax
#import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
@decorator
def function():
...More examples
#import functools
def require_level(minimum):
def decorator(func):
@functools.wraps(func)
def wrapper(player, *args, **kwargs):
if player["level"] < minimum:
return f"{player['name']}: level {minimum} required"
return func(player, *args, **kwargs)
return wrapper
return decorator
@require_level(5)
def enter_world(player, world):
return f"{player['name']} enters world {world}"
print(enter_world({"name": "Emma", "level": 7}, 3))
print(enter_world({"name": "Jack", "level": 2}, 3))def bold(func):
def wrapper():
return f"**{func()}**"
return wrapper
def exclaim(func):
def wrapper():
return f"{func()}!"
return wrapper
@bold
@exclaim
def title():
return "New level"
print(title())This is equivalent to title = bold(exclaim(title)), so the exclamation mark is added before the bolding.
import functools
def count_calls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
wrapper.calls += 1
return func(*args, **kwargs)
wrapper.calls = 0
return wrapper
@count_calls
def check_answer(answer):
return answer == "B"
for answer in ["A", "B", "C"]:
check_answer(answer)
print(check_answer.calls)Good practices
#- Always add
@functools.wraps(func)to the wrapper function. You keep the original’s name and docstring, which makes debugging and reading error messages easier. - The wrapper should accept
*args, **kwargsand return the original’s result. Withoutreturn, every decorated function starts returningNone. - The decorator’s own code runs once, when the function is defined, while the wrapper runs on every call. Put whatever needs to run on every call inside the wrapper.
Related entries
#- defDefines a function: a named block of code with parameters that you can call multiple times.
- *args and **kwargsCollecting any number of positional arguments into a tuple and keyword arguments into a dictionary, plus unpacking a collection at call time.
- @propertyTurns a method into an attribute: the value is computed on read, and an optional setter controls writes.
- global and nonlocalLet a function change variables from the global scope or from an enclosing function.
- lambdaA short anonymous function written as a single expression.
Spotted a mistake or missing an example? Write to us.