Decorators
Wrap a function to add behaviour without changing its code.
Syntax
@decorator
def my_function():
...A decorator is a function that takes another function and returns an enhanced version of it. You apply one with the @ syntax on the line above a function.
Common uses
- Logging when a function runs.
- Timing how long it takes.
- Caching results, checking permissions, and more.
Example
def shout(func):
def wrapper(name):
return func(name).upper()
return wrapper
@shout
def greet(name):
return f'hello, {name}'
print(greet('ada'))
# Output:
# HELLO, ADAWhen to use it
- A @login_required decorator checks authentication before every protected view function runs.
- A @timer decorator wraps a function to log how many milliseconds it takes to execute.
- A @retry decorator automatically retries a flaky network call up to three times on failure.
More examples
Basic decorator
Creates a decorator that wraps greet() to convert its return value to uppercase.
def shout(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
@shout
def greet(name):
return f'hello, {name}!'
print(greet('world')) # HELLO, WORLD!Timer decorator
Wraps a function to measure and print its execution time, preserving the original name with @wraps.
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f'{func.__name__} took {elapsed:.4f}s')
return result
return wrapper
@timer
def slow_sum(n):
return sum(range(n))
slow_sum(1_000_000)Decorator with arguments
Implements a parametrised decorator factory that runs the wrapped function a configurable number of times.
def repeat(times):
def decorator(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say(msg):
print(msg)
say('Hello')
# Hello
# Hello
# Hello
Discussion