Free beta: 60 days of full access, no card needed.84 seats leftSign up free

We use necessary cookies to run the site (sign-in and language). If you accept, we also load Google Analytics to see which pages are used, and Google reCAPTCHA to keep spam off the contact and bug-report forms. Privacy policy

← Blog
How to prepare

Memoization in Python: lru_cache, when it helps and how it differs from a table

· 2026-11-01

Short answer. Memoization is a cache in front of a recursive function. The function stores the answer for each set of arguments. A repeated call returns it from the dictionary. Exponential recursion turns linear or quadratic. In Python it is one line: the lru_cache decorator. Below: when it works, code, a traced example and the bottom-up comparison.

What is memoization?

A recursive function often calls itself with the same arguments. Fibonacci: fib(5) calls fib(4) and fib(3), and fib(4) calls fib(3) again. The call tree grows like 2ⁿ.

Memoization puts a dictionary in front of the function. Answer present: return it. Answer missing: compute, store, return. Each set of arguments is computed once.

This is top-down dynamic programming. Same table, but recursion picks the fill order.

The word is spelled without an r. It comes from memo, a note. The function leaves itself notes about past answers.

Fibonacci with a memo table
fib(5)
call stack
empty
memo: n → fib(n)
n=2n=3n=4n=5
memo
calls = 0cache hits = 0

Compute fib(5). Plain recursion asks for the same fib(n) many times. memo stores each answer the first time it is found.

Speed race
fib(17) by plain recursion and by the same recursion with a memo

Bars: how many times fib(i) was actually computed, i from 0 to 17. A call answered from the memo computes nothing, so its bar stays put.

One operation is one call, and an answer read from the memo counts as one. Every frame, each lane does the same number of operations, so the slow one falls behind.

Plain recursion O(2ⁿ)
0 operations
Recursion with a memo O(n)
0 operations

 

And with more numbers

Operations for n numbers, and roughly how long they take at 100 million simple operations a second. An estimate from the same count, not a measurement.

n = 20
Plain recursion21.9K · under 1 ms
Recursion with a memo39 · under 1 ms
n = 40
Plain recursion331.2M · 3.3 s
Recursion with a memo79 · under 1 ms
n = 60
Plain recursion5T · 14 h
Recursion with a memo119 · under 1 ms

The pattern page: when to use it, and a template in six languages

When does memoization help, and when not?

  • It helps when subproblems repeat: Fibonacci, path counts, string splits.
  • It helps when the function is pure. The answer depends only on the arguments.
  • It does nothing when every call is different: tree traversal, sorting.
  • It fails when arguments are unhashable: lists, dictionaries.

The test is simple. Draw the call tree for a small input. Repeated nodes mean memoization will work.

What does memoization look like on an example?

fib(5) without a cache: 15 calls. With a cache: fib(5), fib(4), fib(3), fib(2), fib(1), fib(0) run once each. Every other call returns from the dictionary. Six real computations.

For n = 40 it is hundreds of millions of calls against forty.

How do you write memoization in Python?

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)

# the same by hand
def fib_memo(n, memo=None):
    memo = {} if memo is None else memo
    if n < 2:
        return n
    if n not in memo:
        memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
    return memo[n]

@lru_cache(maxsize=None)
def unique_paths(r, c):
    if r == 1 or c == 1:
        return 1
    return unique_paths(r - 1, c) + unique_paths(r, c - 1)

def word_break(s, words):
    words = set(words)

    @lru_cache(maxsize=None)
    def can(i):                      # can s[i:] be split
        if i == len(s):
            return True
        return any(s[i:j] in words and can(j) for j in range(i + 1, len(s) + 1))

    return can(0)

lru_cache hashes the arguments. They must be numbers, strings or tuples. Turn lists into tuples first.

How is memoization different from a bottom-up table?

TraitMemoizationBottom-up table
CodeRecursion plus a dictionaryA loop plus an array
OrderRecursion decidesYou decide
Unneeded statesNever computedAll computed
MemoryDictionary plus call stackArray, often shrinkable
LimitPython recursion depthYou must find the order
Pick it whenThe transition is complex, states are sparseStates are many, memory matters

How does memoization solve Unique Paths?

An r by c grid, moves go right or down. How many paths corner to corner? Paths from a cell equal paths from its right neighbour plus its lower neighbour. Base: one row or one column, one path.

There are r · c states, each computed once. Time O(r · c). Without the cache it is exponential.

How does it solve Word Break?

Can a string be split into dictionary words? The function can(i) answers for the suffix from position i. Try each cut j. The prefix must be a word and the rest from j must split. Caching on i gives O(n²) plus substring checks.

When is memoization not enough?

When n is above ten thousand. Recursion goes n deep, and Python stops it. Rewrite bottom-up as a loop. The transition stays the same, only the code shape changes.

The second case is memory. The dictionary holds every state. A loop with two variables holds two numbers. Interviewers ask about this after the working solution.

What mistakes break memoization?

  • A mutable default argument memo={}. The cache leaks between top-level calls.
  • The function reads an outer variable that the cache key ignores.
  • Recursion deeper than Python's limit. Raise it or switch to a loop.
  • Caching on objects compared by identity. Equal data produces different keys.

How do you explain memoization in an interview?

Write the plain recursion first. Then show that subproblems repeat. Then add the dictionary. Three steps, and the interviewer sees your reasoning.

Give the complexity as distinct states times work per call. For Fibonacci that is O(n).

Drill this pattern on the recursion with memoization page. The bottom-up form lives on the dynamic programming page.

FAQ

What is the difference between lru_cache and cache?

functools.cache is lru_cache with no size limit. It arrived in Python 3.9. For interviews there is no difference.

How do you memoize a method?

The decorator works, but self becomes part of the key. Better to move the computation into a nested function inside the method.

Can you memoize a function with two arguments?

Yes. The key becomes the tuple of arguments. The state count is the product of the ranges, so watch memory.

Where does memoization show up in real systems?

React memoizes a component's render for the same inputs. Parsers cache partial results for grammars. Any query cache keyed by parameters is the same idea.