reduce() in Python: functools.reduce With Examples

reduce() in Python combines the items of an iterable into a single value by applying a two-argument function over and over: first to items one and two, then to that result and item three, and so on. It lives in functools:

from functools import reduce
import operator

reduce(operator.add, [5, 10, 15])        # ((5 + 10) + 15) = 30

I printed the intermediate steps so you can watch reduce() work; the screenshots come from Python 3.12.5 in a Windows Command Prompt.

You’ll see exactly how it steps through a list, the initializer and the empty-list error, practical uses like merging and pipelines, and when a built-in or a loop beats it.

How reduce() works, step by step

The function you pass gets two arguments: the running result so far, and the next item. Printing inside it shows every step:

from functools import reduce

def add(total, amount):
    print(f"  {total:>8} + {amount:>7} = {total + amount}")
    return total + amount

sales = [1240.50, 860.00, 1530.00, 410.25]

print("result:", reduce(add, sales))

Output:

    1240.5 +   860.0 = 2100.5
    2100.5 +  1530.0 = 3630.5
    3630.5 +  410.25 = 4040.75
result: 4040.75
Command Prompt output of Python functools reduce adding four sales figures one step at a time
Each line takes the previous total and adds the next sale. The last total is what reduce() returns.

Four sales took three calls. The first call uses the first two items, and every later call combines the previous result with one new item.

The initializer and ‘reduce() of empty iterable with no initial value’

An optional third argument gives reduce() a starting value. It’s also what saves you when the list is empty:

from functools import reduce
import operator

print(reduce(operator.add, [5, 10, 15]))          # 30
print(reduce(operator.add, [5, 10, 15], 100))     # starts from 100: 130
print(reduce(operator.add, [], 0))                # empty, with an initializer: 0

try:
    reduce(operator.add, [])                      # empty, no initializer
except TypeError as err:
    print("TypeError:", err)

Output:

30
130
0
TypeError: reduce() of empty iterable with no initial value
Command Prompt output of Python reduce with and without an initializer, including the TypeError for an empty iterable
With no items and no initializer there’s nothing to return, so reduce() raises TypeError.

If there’s any chance the list could be empty, pass an initializer: 0 for addition, 1 for multiplication, an empty dict or list for merging.

Should you use reduce() or sum(), max() and math.prod()?

For the common cases, you’ll find a built-in that’s shorter, faster and easier to read. reduce() can do all of them, but you don’t need it to:

from functools import reduce
import math
import operator

nums = [4, 9, 2, 7]

print("product, reduce :", reduce(operator.mul, nums, 1))
print("product, prod   :", math.prod(nums))
print("largest, reduce :", reduce(lambda a, b: a if a > b else b, nums))
print("largest, max    :", max(nums))
print("total, reduce   :", reduce(operator.add, nums))
print("total, sum      :", sum(nums))

Output:

product, reduce : 504
product, prod   : 504
largest, reduce : 9
largest, max    : 9
total, reduce   : 22
total, sum      : 22

Use sum(), max(), min(), math.prod(), any() or all() when one fits. Python 3 moved reduce() out of the built-ins and into functools partly for this reason; an explicit loop is often clearer.

Merging dictionaries and counters with reduce()

Where reduce() earns its place is combining a list of things that don’t have a built-in shortcut, like inventory counts from several stores:

from functools import reduce
from collections import Counter

austin = Counter({"mugs": 14, "pens": 120})
dallas = Counter({"mugs": 6, "notebooks": 37})
houston = Counter({"pens": 80, "notebooks": 12})

total_stock = reduce(lambda a, b: a + b, [austin, dallas, houston])
print(dict(total_stock))

settings = [{"region": "us-east"}, {"retries": 3}, {"region": "us-west"}]
print(reduce(lambda a, b: a | b, settings))       # later dicts win (Python 3.9+)

Output:

{'mugs': 20, 'pens': 200, 'notebooks': 49}
{'region': 'us-west', 'retries': 3}

Counter objects add key by key, so three stores become one stock count. The | operator merges dictionaries, with later values winning, which is how the region ends up as us-west.

Using reduce() to apply a pipeline of functions

With an initializer, reduce() can pass a value through a list of functions in order. Here a $49.99 item gets 10% off, then Austin’s 8.25% sales tax, then rounding:

from functools import reduce

def discount(price):
    return price * 0.90          # 10% off

def add_tax(price):
    return price * 1.0825        # 8.25% Austin sales tax

def to_cents(price):
    return round(price, 2)

steps = [discount, add_tax, to_cents]

price = 49.99
final = reduce(lambda value, step: step(value), steps, price)
print(f"${price:.2f} -> ${final:.2f}")

Output:

$49.99 -> $48.70
Command Prompt output of Python reduce applying discount, Texas sales tax and rounding functions to a price in order
Three functions, applied in the order they’re listed, starting from the price.

Adding a step is just adding a function to the list. I like this shape for price rules and data-cleaning steps that change over time.

reduce() vs itertools.accumulate()

reduce() only returns the final value. If you want every intermediate result, such as a running balance, use itertools.accumulate(), which takes the same kind of function:

from itertools import accumulate
from functools import reduce
import operator

deposits = [150, 150, 200, 150]

print("reduce     :", reduce(operator.add, deposits))         # just the final total
print("accumulate :", list(accumulate(deposits)))            # every running total
print("max so far :", list(accumulate([3, 7, 2, 9, 4], max)))

Output:

reduce     : 650
accumulate : [150, 300, 500, 650]
max so far : [3, 7, 7, 9, 9]
Command Prompt output comparing Python reduce returning a single total with itertools accumulate returning running totals
Same deposits: one total from reduce(), every running total from accumulate().

Using reduce() with lambda vs the operator module

Most examples you’ll find pass a lambda, like lambda a, b: a + b. The operator module has ready-made versions, operator.add and operator.mul, which are a little faster and read better. Lambda functions in Python covers lambdas themselves.

reduce() often travels with map() and filter(): transform, keep, then combine. The map() function in Python covers the first step.

When to use reduce() in Python

GoalBest choice
Totalsum(items)
Productmath.prod(items)
Largest or smallestmax() / min()
Any or all trueany() / all()
Running totalsitertools.accumulate()
Merge many dicts or Countersreduce() with | or +
Apply functions in orderreduce(lambda v, f: f(v), steps, start)
Anything with several steps per itemA plain for loop

Related functional-programming tools in Python:

Frequently asked questions

What does reduce() do in Python?

It applies a two-argument function cumulatively to the items of an iterable, reducing them to a single value, such as a total or a merged dictionary.

Where is reduce() in Python 3?

In the functools module: from functools import reduce. It was a built-in in Python 2.

How do I fix ‘reduce() of empty iterable with no initial value’?

Pass an initializer as the third argument, such as reduce(operator.add, items, 0), so there’s a value to return when the iterable is empty.

What is the difference between reduce() and accumulate()?

reduce() returns only the final result. itertools.accumulate() returns every intermediate result as well.

Is reduce() faster than a for loop?

Not meaningfully. Use whichever reads better; for sums, products and maximums, the built-ins beat both.

Can I use reduce() with a lambda?

Yes, the function can be any callable that takes two arguments. The functools.reduce() documentation shows the exact signature.