reduce expresses folds like products, merges and compositions that have no dedicated built-in.
reduce(function, iterable, initial) applies a two-argument function cumulatively: it combines the first two items, then that result with the third, and so on, returning a single value.
reduce(operator.mul, [1, 2, 3, 4]) # ((1*2)*3)*4 = 24The initial value is placed before the items. Supply it whenever the iterable might be empty: without it, reduce on an empty iterable raises TypeError. It also sets the result type, for example reduce(merge, dicts, {}).
Many reductions have clearer built-ins, which is why reduce was moved out of the builtins in Python 3:
- sums:
sum(), products:math.prod() max(),min(),any(),all()- joining strings:
"".join() - merging dicts:
{**a, **b}ora | b
Use reduce for folds without a built-in: combining sets with operator.or_, deep getattr chains, composing a list of functions, or applying a sequence of transformations to a value.
If you need every intermediate result, not just the last, use itertools.accumulate. If the lambda you pass gets complicated, an explicit loop is usually easier to read.
Example
import operator
from functools import reduce
print(reduce(operator.mul, [1, 2, 3, 4])) # 24
print(reduce(operator.or_, [{1, 2}, {2, 3}, {4}])) # {1, 2, 3, 4}
print(reduce(operator.add, [], 0)) # 0
try:
reduce(operator.add, [])
except TypeError as e:
print("empty without initial") # empty without initial
def compose(*funcs):
return reduce(lambda f, g: lambda x: g(f(x)), funcs)
pipeline = compose(str.strip, str.lower, str.title)
print(pipeline(" hELLO wORLD ")) # Hello World
config = {"db": {"primary": {"host": "10.0.0.5"}}}
print(reduce(operator.getitem, ["db", "primary", "host"], config)) # 10.0.0.5Remember
reduce(f, items, initial)folds items into one value left to right- Always pass an initial value if the iterable may be empty
- Prefer sum, math.prod, max, any or join when they fit
Watch out for
- Using reduce with a complex lambda where a for-loop would be clearer.