Lesson 6 · Python standard library deep dive
Python functools Explained: lru_cache, partial & wraps | Standard Library #6
Video six of the twenty-five-part series: functools, tools for working with and building higher-order functions. reduce, partial, caching with lrucache,…
- CoursePython standard library deep dive
- Lesson6 of 24
- Video18 min
- FormatJupyter notebook · 13 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPython Standard Library Deep-Dive, Video 6: functools#
- Video six of the twenty-five-part series: functools, tools for working with and building higher-order functions.
- reduce, partial, caching with lru_cache, cached_property, singledispatch, and more.
- Let's get into it.
Part 1: What functools Offers#
import functools
print(dir(functools))
Part 2: reduce()#
from functools import reduce
nums = [1, 2, 3, 4, 5]
total = reduce(lambda acc, n: acc + n, nums)
print(total)
product = reduce(lambda acc, n: acc * n, nums)
print(product)
product_with_start = reduce(lambda acc, n: acc * n, nums, 10)
print(product_with_start)
words = ['Python', 'is', 'genuinely', 'fun']
longest = reduce(lambda a, b: a if len(a) >= len(b) else b, words)
print(longest)
nested = [[1, 2], [3, 4], [5]]
flattened = reduce(lambda acc, lst: acc + lst, nested, [])
print(flattened)
Part 3: partial()#
from functools import partial
def power(base, exponent):
return base ** exponent
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5))
print(cube(5))
print(square(10))
def greet(greeting, name, punctuation='!'):
return f'{greeting}, {name}{punctuation}'
say_hello = partial(greet, 'Hello')
print(say_hello('Ana'))
say_hello_excited = partial(greet, 'Hello', punctuation='!!!')
print(say_hello_excited('Sam'))
Part 4: wraps() and the Decorator Problem#
def broken_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@broken_decorator
def add(a, b):
'''Adds two numbers.'''
return a + b
print(add.__name__)
print(add.__doc__)
from functools import wraps
def fixed_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@fixed_decorator
def multiply(a, b):
'''Multiplies two numbers.'''
return a * b
print(multiply.__name__)
print(multiply.__doc__)
Part 5: lru_cache() and cache()#
from functools import lru_cache
call_count = 0
@lru_cache(maxsize=None)
def slow_square(n):
global call_count
call_count += 1
return n * n
print(slow_square(5))
print(slow_square(5))
print(slow_square(6))
print(call_count)
print(slow_square.cache_info())
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30))
print(fibonacci.cache_info())
Part 6: cached_property()#
from functools import cached_property
class Report:
def __init__(self, values):
self.values = values
self.compute_count = 0
@cached_property
def total(self):
self.compute_count += 1
return sum(self.values)
r = Report([10, 20, 30])
print(r.total)
print(r.total)
print(r.compute_count)
Part 7: singledispatch()#
from functools import singledispatch
@singledispatch
def describe(value):
return f'Some value: {value}'
@describe.register
def _(value: int):
return f'An integer: {value}'
@describe.register
def _(value: list):
return f'A list with {len(value)} items'
print(describe('hello'))
print(describe(42))
print(describe([1, 2, 3]))
Part 8: total_ordering()#
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor):
self.major = major
self.minor = minor
def __eq__(self, other):
return (self.major, self.minor) == (other.major, other.minor)
def __lt__(self, other):
return (self.major, self.minor) < (other.major, other.minor)
v1 = Version(1, 5)
v2 = Version(2, 0)
print(v1 < v2)
print(v1 <= v2)
print(v1 > v2)
print(v1 >= v2)
Part 9: cmp_to_key()#
from functools import cmp_to_key
def compare_by_length_then_alpha(a, b):
if len(a) != len(b):
return len(a) - len(b)
if a < b:
return -1
if a > b:
return 1
return 0
words = ['banana', 'fig', 'kiwi', 'apple', 'date']
sorted_words = sorted(words, key=cmp_to_key(compare_by_length_then_alpha))
print(sorted_words)
Wrap-Up: What You Learned#
- reduce collapses an iterable into a single value using any two-argument function.
- partial locks in arguments ahead of time to build simpler, reusable callables.
- wraps preserves a decorated function's real name and docstring.
- lru_cache and cache memoize function results automatically; cache_info reports on their behavior.
- cached_property computes a class attribute once and stores the result on the instance.
- singledispatch and total_ordering reduce boilerplate for type-based dispatch and rich comparisons.
- cmp_to_key bridges an old-style comparison function into a modern sort key.
- That wraps up functools. Next up: re, for regular expressions.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



