functools Interview Questions & Answers

5 questions Updated 2026-06-18

Python interview questions on functools — lru_cache and cache, partial, wraps, reduce, cached_property, and singledispatch.

functools.lru_cache is a decorator that memoizes a function — it stores results keyed by the arguments and returns the cached value on repeat calls, avoiding recomputation. maxsize caps how many results are kept, evicting the least-recently-used entries; lru_cache(maxsize=None) (or functools.cache in 3.9+) caches without limit.

from functools import lru_cache

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

fib(50)                 # fast — each n computed once
fib.cache_info()        # hits, misses, maxsize, currsize
fib.cache_clear()       # reset the cache

Arguments must be hashable (they're used as dict keys), and the function should be pure — caching an impure function returns stale results. Rule of thumb: use it for expensive, deterministic calls with repeated inputs.

functools.partial creates a new callable with some arguments of an existing function pre-filled. It's a clean way to specialize a general function without writing a wrapper or a lambda.

from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)   # exp fixed to 2
cube   = partial(power, exp=3)

square(5)    # 25
cube(2)      # 8

Partials are handy for callbacks, event handlers, and configuring functions passed to map/sorted/GUI bindings. Rule of thumb: reach for partial when you keep calling the same function with one or two fixed arguments.

A decorator replaces the original function with a wrapper, which loses the original's metadata — its __name__, __doc__, and signature now point at the wrapper. functools.wraps copies that metadata from the wrapped function onto the wrapper, so introspection, debugging, and documentation still work.

from functools import wraps

def log(fn):
    @wraps(fn)                 # copy fn's metadata to wrapper
    def wrapper(*args, **kwargs):
        print("calling", fn.__name__)
        return fn(*args, **kwargs)
    return wrapper

@log
def greet(): "say hi"
greet.__name__   # 'greet'  (without @wraps it'd be 'wrapper')

Without @wraps, tools like help(), tracebacks, and doc generators show the wrapper instead of the real function. Rule of thumb: always add @wraps(fn) to the inner function of any decorator.

functools.reduce repeatedly applies a two-argument function across an iterable, folding it down to a single accumulated value. It carries a running result, combining it with each element in turn; an optional initializer seeds the accumulator (and makes it safe on empty iterables).

from functools import reduce

reduce(lambda acc, x: acc + x, [1, 2, 3, 4])      # 10
reduce(lambda acc, x: acc * x, [1, 2, 3, 4], 1)   # 24, seeded with 1

For common folds Python already has built-ins (sum, max, min, any, all) that are clearer and faster — reduce shines for custom accumulation logic. Rule of thumb: prefer a built-in or an explicit loop unless the fold is genuinely bespoke, since reduce can hurt readability.

functools.cached_property turns a method into a property whose result is computed once and stored on the instance, so later accesses are cheap. The cached value lives in the instance __dict__ and is recomputed only if you delete it. functools.singledispatch creates a generic function that dispatches to different implementations based on the type of the first argument — function overloading by type.

from functools import cached_property, singledispatch

class Dataset:
    @cached_property
    def stats(self):           # expensive; runs once per instance
        return expensive_scan(self.data)

@singledispatch
def describe(x): return f"value: {x}"
@describe.register
def _(x: list): return f"list of {len(x)}"
@describe.register
def _(x: int): return f"int {x}"

describe([1, 2])   # 'list of 2'
describe(7)        # 'int 7'

cached_property trades memory for speed on costly, stable computations; singledispatch keeps type-specific behaviour in separate, registerable functions instead of a big if/isinstance chain. Rule of thumb: cache derived values that don't change, and dispatch when behaviour varies cleanly by argument type.

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.