Fundamentals 9 min read

A Guide to Useful Python Decorators

This article introduces several practical Python decorators—including @lru_cache, @jit, @do_twice, @count_calls, @dataclass, @singleton, @use_unit, and @singledispatch—explaining their purposes, benefits, and providing code examples to demonstrate how they can improve performance, readability, and functionality in Python programs.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
A Guide to Useful Python Decorators

Python’s flexibility is largely due to its powerful decorator feature, which can modify the behavior of functions and classes.

Below are several practical decorators, each illustrated with code examples.

1. @lru_cache

Provided by functools , @lru_cache caches the results of function calls, dramatically speeding up repeated executions such as recursive factorial calculations.

<code>def factorial(n):
    return n * factorial(n-1) if n else 1
</code>
<code>@lru_cache
def factorial(n):
    return n * factorial(n-1) if n else 1
</code>

The first few results are stored, so subsequent calls retrieve cached values instead of recomputing.

2. @jit

The Just‑In‑Time (JIT) decorator from the Numba library compiles Python code to machine code at runtime, reducing overhead and accelerating compute‑intensive tasks such as Monte Carlo π estimation.

<code>from numba import jit
import random

@jit(nopython=True)
def monte_carlo_pi(nsamples):
    acc = 0
    for i in range(nsamples):
        x = random.random()
        y = random.random()
        if (x**2 + y**2) < 1.0:
            acc += 1
    return 4.0 * acc / nsamples
</code>

3. @do_twice

This simple decorator runs the wrapped function twice, which can be handy for quick performance checks or debugging.

<code>from decorators import do_twice

@do_twice
def timerfunc():
    %timeit factorial(15)
</code>

4. @count_calls

Tracks how many times a function has been invoked, providing useful runtime diagnostics.

<code>from decorators import count_calls

@count_calls
def function_example():
    print("Hello World!")

function_example()
function_example()
function_example()
</code>

5. @dataclass

The @dataclass decorator from the dataclasses module automatically generates common special methods such as __init__ , reducing boilerplate when defining classes.

<code>from dataclasses import dataclass

@dataclass
class Food:
    name: str
    unit_price: float
    stock: int = 0

    def stock_value(self) -> float:
        return self.stock * self.unit_price
</code>

6. @singleton

Ensures a class has only one instance throughout a program, effectively providing a global‑like object without using module‑level variables.

<code>def singleton(cls):
    instances = {}
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class MyClass:
    def func(self):
        pass
</code>

7. @use_unit

A custom decorator that attaches a physical unit (using the pint library) to a function’s return value, useful in scientific calculations.

<code>def use_unit(unit):
    """Return a Quantity with the given unit."""
    use_unit.ureg = pint.UnitRegistry()
    def decorator_use_unit(func):
        @functools.wraps(func)
        def wrapper_use_unit(*args, **kwargs):
            value = func(*args, **kwargs)
            return value * use_unit.ureg(unit)
        return wrapper_use_unit
    return decorator_use_unit

@use_unit("meters per second")
def average_speed(distance, duration):
    return distance / duration
</code>

8. @singledispatch

The functools.singledispatch decorator enables function overloading based on the type of the first argument, simplifying code that must handle multiple input types.

<code>@singledispatch
def fun(arg, verbose=False):
    if verbose:
        print("Let me just say,", end=" ")
    print(arg)

@fun.register
def _(arg: int, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)

@fun.register
def _(arg: list, verbose=False):
    if verbose:
        print("Enumerate this:")
    for i, elem in enumerate(arg):
        print(i, elem)
</code>
Pythonsingletonlru-cachedataclass
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.