functools & Functional Programming Tools

15 questions found

What does functools.reduce's initializer argument do, and why should you usually provide one?

Intermediate
The optional third argument to reduce serves as the starting accumulator value and is returned as-is if the iterable is empty. Without it, reduce raises TypeError on an empty iterable and uses the first element as the initial accumulator, which can produce subtly wrong results for operations that aren't naturally idempotent.
from functools import reduce

# Without initializer: fails on empty list
try:
    reduce(lambda a,b: a+b, [])
except TypeError as e:
    print('Error:', e)

# With initializer: safe
print(reduce(lambda a,b: a+b, [], 0))  # 0
Real-world example An analytics pipeline reducing a possibly-empty list of daily sales always passes 0 as the initializer to avoid crashing on days with no data.

Common follow-ups: What's the type of the initializer relative to the sequence elements?;How does reduce compare to itertools.accumulate?

Exception Handling;Comprehensions & Generators

How do you use functools.partial to fix keyword arguments for logging or callback functions?

Beginner
partial(func, **kwargs) pre-binds keyword arguments, returning a new callable that only needs the remaining arguments when invoked. This is a common pattern for adapting a general-purpose function to an event system or callback API that calls it with a fixed signature.
from functools import partial
import logging

def log(message, level='INFO'):
    print(f'[{level}] {message}')

warn = partial(log, level='WARNING')
warn('Disk space low')  # [WARNING] Disk space low
Real-world example A task scheduler registers partial(send_email, template='reminder') as the callback so the scheduler only needs to supply the recipient at call time.

Common follow-ups: Can partial objects be introspected to see bound arguments?;How does partial interact with **kwargs at call time?

Decorators;Closures

How does functools.cmp_to_key bridge old-style comparison functions with Python 3's key-based sorting?

Advanced
Python 3 removed the cmp parameter from sorted() and list.sort() in favor of key functions. cmp_to_key wraps a legacy two-argument comparison function (returning negative, zero, or positive) into a key-function-compatible object, letting you reuse comparator logic (e.g., from Python 2 code or complex multi-field tie-breaking) with modern sorting.
from functools import cmp_to_key

def compare(a, b):
    if a['priority'] != b['priority']:
        return b['priority'] - a['priority']
    return a['name'] < b['name'] and -1 or 1

items = [{'name':'b','priority':1}, {'name':'a','priority':2}]
sorted_items = sorted(items, key=cmp_to_key(compare))
Real-world example A task queue with complex tie-breaking rules (priority descending, then name ascending) uses cmp_to_key to port an existing comparator into sorted().

Common follow-ups: Why did Python 3 remove the cmp argument?;When is a plain key function simpler than cmp_to_key?

Data Types & Structures;Comprehensions & Generators

What is the practical difference between using functools.reduce and a plain for-loop for accumulation?

Intermediate
Functionally they're equivalent, but reduce expresses the accumulation as a single expression which can be more concise for simple, well-known operations (sum, product, max), while a for-loop is usually more readable for anything involving multiple steps, side effects, or conditional logic within the accumulation. PEP 8 style favors loops or built-ins over reduce for clarity.
# reduce version
from functools import reduce
total = reduce(lambda acc, x: acc + x, nums, 0)

# loop version -- often preferred
total = 0
for x in nums:
    total += x
Real-world example A code review flags a nested reduce-of-reduce expression as hard to read and requests it be rewritten as explicit nested loops for maintainability.

Common follow-ups: Guido van Rossum's stance on reduce readability?;When does reduce genuinely improve clarity?

Comprehensions & Generators;Iterators & the Iterator Protocol

How can functools.lru_cache be used with typed=True, and what does that change?

Advanced
By default lru_cache treats arguments that compare equal (like 1 and 1.0) as the same cache key. Setting typed=True makes it cache them separately based on argument type as well as value, which matters when a function's behavior or return type genuinely differs between an int and a float input.
from functools import lru_cache

@lru_cache(maxsize=None, typed=True)
def describe(x):
    return f'{x} is a {type(x).__name__}'

print(describe(1))    # cached separately from...
print(describe(1.0))  # ...this call
Real-world example A unit-conversion function that behaves differently for int vs float precision inputs uses typed=True so results aren't incorrectly shared across types.

Common follow-ups: What's the performance cost of typed=True?;How are unhashable arguments handled by lru_cache?

Data Types & Structures;Decorators

Showing 11–15 of 15