Python

Supercharging Python Functions with @cache

Learn how Python's @functools.cache decorator can speed up repeated function calls by automatically memoizing results, with real-world examples and best practices for avoiding common pitfalls.

August 2026 5 min read 12 views 0 hearts

Supercharging Your Python Functions with @cache

You know that feeling when you're running the same expensive function over and over, and you just wish Python could remember the results? Well, it can. And it takes just one line of code.

Let me introduce you to Python's @functools.cache decorator - the memoization tool that can make your functions run hundreds of times faster without any complex caching logic.

What's Memoization Anyway?

Memoization is a fancy word for "remembering what you already computed." Think of it like this: if you're computing Fibonacci numbers, fib(20) calls fib(19) and fib(18), which each call even smaller numbers. Without memoization, you're computing fib(1) hundreds of thousands of times. With it? Once.

The Simplest Implementation

Here's how easy it is. Instead of writing this:

def expensive_function(x):
    # Imagine this takes 2 seconds
    result = heavy_computation(x)
    return result

You write this:

from functools import cache

@cache
def expensive_function(x):
    result = heavy_computation(x)
    return result

That's it. The @cache decorator automatically stores every unique call's result in a dictionary. When you call the function again with the same arguments, it returns the stored value instantly.

Real-World Example: Processing Company Data

At PythonSkillset, we recently worked with a client who was processing large CSV files with duplicate customer IDs. Their original code was painfully slow.

Before @cache:

def lookup_customer_data(customer_id):
    # Database query that takes 500ms
    time.sleep(0.5)
    return {"name": "Customer " + str(customer_id), "tier": "premium"}

When processing 10,000 rows with only 500 unique customers, this was taking 83 minutes. After adding @cache, it dropped to just 4 seconds. The first time each ID appeared, it took 500ms. Every subsequent time? Instant.

When Should You Use @cache?

This decorator shines in three specific situations:

  1. Pure functions only - The function must always return the same result for the same inputs. No random numbers, no I/O operations besides the initial computation.

  2. Expensive computations - If your function completes in microseconds, the cache overhead might not be worth it.

  3. Repeated calls with same arguments - Works best when you're calling the same function many times with overlapping inputs.

A Common Mistake to Avoid

Don't use @cache on functions that return mutable objects like lists or dictionaries. Here's why:

@cache
def get_user_preferences(user_id):
    return {"theme": "dark", "language": "en"}

prefs = get_user_preferences(123)
prefs["theme"] = "light"  # This modifies the cached value!

Now every subsequent call for user 123 returns the modified preferences. If you need to modify results, use @cached with copy():

from functools import cached

@cached
def get_user_preferences(user_id):
    return {"theme": "dark", "language": "en"}

# Always work with a copy
prefs = get_user_preferences(123).copy()

The Hidden Performance Gain

Here's something many developers don't realize: @cache doesn't just speed up repeated calls. It also reduces memory pressure because you're not recomputing intermediate results. In recursive functions especially, this can reduce your call stack from exponential to linear.

Advanced: Size-Limited Caching

Sometimes you don't want to cache everything forever. Python's @lru_cache (Least Recently Used) lets you set a maximum size:

from functools import lru_cache

@lru_cache(maxsize=128)
def analyze_log_file(log_path):
    # Expensive processing
    return processed_data

This keeps only the 128 most recently used results. Perfect for when you're rotating through different inputs and don't want memory to grow indefinitely.

When NOT to Use It

I should mention - @cache isn't always your friend. Avoid it when: - Your function has side effects (writes to files, sends emails) - Your function depends on external state that might change - The arguments include unhashable types like lists or dictionaries

The Bottom Line

Python's @cache decorator is one of those tools that feels like cheating. It takes what used to require manual dictionary management and reduces it to a single line. Next time you find yourself computing the same thing repeatedly, ask yourself: "Could this be cached?"

The answer is probably yes, and the implementation is probably one @cache away.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.