Python Decorators Cheat Sheet
This page contains a condensed overview of Python decorators. It covers how functions work as objects, how to write a basic decorator, how to handle arguments and preserve metadata, decorators with arguments, class-based decorators, stacking, and the most useful built-in decorators. You can also download the information as a printable cheat sheet:
Free Bonus: Python Decorators Cheat Sheet
Get a Python Decorators Cheat Sheet (PDF) and keep the essentials at hand: writing decorators, handling arguments, preserving metadata, decorator factories, and the built-in decorators you'll use most:
Practice what you learn with hands-on coding exercises, quizzes, and guided learning paths. Not sure where to begin? Start here.
New to decorators?
Functions Are Objects
- Functions are first-class: pass them around like any value
- Inner functions remember their enclosing scope (closures)
funcrefers to a function,func()calls it- Pass functions as arguments:
sorted(words, key=len)
Return a Function
def make_adder(n):
def add(x):
return x + n
return add
add_five = make_adder(5)
add_five(10) # 15
Want to go deeper on closures?
Basic Decorator
- A decorator takes a function and returns a new function
@decoratoris shorthand forfunc = decorator(func)- Return the inner function, don’t call it
- After decorating, the name
hellopoints towrapper
Define and Apply
def shout(func):
def wrapper():
result = func()
return result.upper()
return wrapper
@shout
def hello():
return "hello"
hello() # 'HELLO'
Think you’ve got the basics down?
Arguments and Return Values
- Accept
*args, **kwargsandreturnthe wrapped result
General-Purpose Wrapper
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_call
def add(a, b=0):
return a + b
add(2, b=3) # Prints Calling add -> 5
What does the wrapper need?
Preserve Metadata
- Wrapping hides
.__name__and the docstring - Fix it with
@functools.wraps(func)on the wrapper
Use functools.wraps
import functools
def log_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@log_call
def add(a, b):
"""Add two numbers."""
return a + b
add.__name__ # 'add'
add.__doc__ # 'Add two numbers.'
Wondering why wraps matters?
Free Bonus: Download the Python Decorators Cheat Sheet PDF and keep the essentials at hand.
Decorators With Arguments
- An outer function takes the arguments and returns the decorator
Decorator Factory
import functools
def repeat(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
value = func(*args, **kwargs)
return value
return wrapper
return decorator
@repeat(times=3)
def wave():
print("Hi!")
wave() # Prints Hi! three times
Still fuzzy on decorator factories?
Stateful and Class-Based
- Store state on the wrapper or in a class with
.__call__() - Any callable can be a decorator
Count Calls With a Class
import functools
class CountCalls:
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
return self.func(*args, **kwargs)
@CountCalls
def ping():
return "pong"
ping(); ping()
ping.calls # 2
Closure or class?
Stacking and Classes
- Decorators apply bottom-up: the closest one wraps first
- Decorating a class replaces the class, not its methods
Stack Decorators
@log_call
@repeat(times=2)
def hello():
print("Hello")
# Same as: hello = log_call(repeat(times=2)(hello))
Decorate a Class
REGISTRY = {}
def register(cls):
REGISTRY[cls.__name__] = cls
return cls
@register
class Plugin: ...
# REGISTRY == {'Plugin': <class 'Plugin'>}
Ready to test yourself on stacking?
Built-in Decorators
| Decorator | Use it to |
|---|---|
@property |
Getter as an attribute |
@staticmethod |
Method without self |
@classmethod |
Gets cls, not self |
@functools.cache |
Memoize return values |
@functools.lru_cache(maxsize=128) |
Memoize with a size limit |
@dataclasses.dataclass |
Generate __init__ etc. |
Memoize a Recursive Function
import functools
@functools.cache
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
fib(80) # 23416728348467685
Want to put these to work?
Ready to go beyond the cheat sheet?
You can download this information as a printable cheat sheet:
Free Bonus: Python Decorators Cheat Sheet
Get a Python Decorators Cheat Sheet (PDF) and keep the essentials at hand: writing decorators, handling arguments, preserving metadata, decorator factories, and the built-in decorators you'll use most: