Skip to content
  • Decorators · Python

Decorators

LearnViaNotes @learnvianotes· 1 min read

A decorator takes a function and returns a new function that wraps it — adding behaviour like timing, logging, caching or access checks without touching the original code.

Writing one

Python
import functools

def shout(func):
    @functools.wraps(func)            # keep func's name and docstring
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

@shout
def greet(name):
    """Say hello."""
    return f"hello, {name}"

print(greet("asha"))                     # HELLO, ASHA
print(greet.__name__, greet.__doc__)     # greet Say hello.

Decorators with arguments

Add one more level: a function that takes the arguments and returns the decorator.

Python
import functools

def retry(times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except ValueError:
                    if attempt == times:
                        raise
        return wrapper
    return decorator

calls = {"n": 0}

@retry(times=3)
def flaky():
    calls["n"] += 1
    if calls["n"] < 3:
        raise ValueError("not yet")
    return "ok"

print(flaky(), calls["n"])   # ok 3

A timing decorator

Python
import functools, time

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            elapsed = time.perf_counter() - start
            wrapper.last_ms = round(elapsed * 1000, 3)
    return wrapper

@timed
def work():
    return sum(range(100_000))

print(work())                            # 4999950000
print(isinstance(work.last_ms, float))   # True

Stacking & built-in decorators

Stacked decorators apply bottom-up: @a over @b over def f means f = a(b(f)).

DecoratorPurpose
@propertyExpose a method as a read-only (or validated) attribute
@classmethod / @staticmethodMethods that receive the class / nothing instead of self
@functools.cache / lru_cacheMemoization
@dataclasses.dataclassGenerate __init__, __repr__, __eq__ for a class
@contextlib.contextmanagerTurn a generator into a with-statement context manager

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report