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:

Python Decorators Cheat Sheet

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)
  • func refers to a function, func() calls it
  • Pass functions as arguments: sorted(words, key=len)
Language: Python Filename: 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
  • @decorator is shorthand for func = decorator(func)
  • Return the inner function, don’t call it
  • After decorating, the name hello points to wrapper
Language: Python Filename: 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, **kwargs and return the wrapped result
Language: Python Filename: 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
Language: Python Filename: 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?

Decorators With Arguments

  • An outer function takes the arguments and returns the decorator
Language: Python Filename: 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
Language: Python Filename: 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
Language: Python Filename: Stack Decorators
@log_call
@repeat(times=2)
def hello():
    print("Hello")

# Same as: hello = log_call(repeat(times=2)(hello))
Language: Python Filename: 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.
Language: Python Filename: 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:

Python Decorators Cheat Sheet