The reduce() function from the functools module in Python’s standard library is typically recommended for tasks that cannot be easily expressed using built-in language constructs such as comprehensions (list, set, or dict), generator expressions, or aggregation functions like all(), any(), len(), max(), min(), or sum().
In the figure below, you can see the formula and block diagram of a simple cumulative addition process.

If the task is to compute the sum of the sequence 1, 2, 3, this can easily be done using reduce(), since all we need to do is pass a function that performs addition along with the sequence itself. The only strict requirement for the function is that it must accept two positional arguments. The corresponding code is this:
|
1 2 3 4 5 6 7 8 |
from functools import reduce def fn(y, x): return y + x print(reduce(fn, range(1, 4), 0)) # Result: 6 |
But what if the formula derived from our task depends on the index of each item, and may also require the intermediate results to be multiplied by a constant? This situation is illustrated in this figure:

The difficulty here is that within reduce(), there is no direct way to pass the current index to the function being applied cumulatively. In addition, intermediate values are not accessible externally, so we cannot multiply them by a constant from outside the function.
Even so, reduce() can still be used in such cases—provided that the function is defined appropriately. As mentioned, the only requirement is that it must accept two positional arguments. However, it is perfectly valid to define additional parameters with default values. One of these can hold the constant multiplier, and the other can provide access to the current index. The question is: how can all of this be implemented?
The core issue is that the function passed to reduce() is called repeatedly. Therefore, to track the current index across invocations, we need an object that can retain state between calls. The code snippet below shows two possible solutions. The first one uses the count() iterator from the itertoolsmodule, while the second one relies on a list.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from functools import reduce from itertools import count def fn1(y, x, ctn=count(1), c=0.5): n = next(ctn) return c*y+n*x print(reduce(fn1, range(1, 4), 0)) # Result: 11.25 def fn2(y, x, n=[0], c=0.5): n[0] += 1 return c*y+n[0]*x print(reduce(fn2, range(1, 4), 0)) # Result: 11.25 |
On their own, these solutions would not be sufficient. However, Python has a particular behavior that works in our favor: the default value of a function parameter is created only once, when the function is defined—not each time it is called. This ensures that the count() or list object remains consistent across all internal calls during the execution of reduce().
This technique is discussed in more detail in the subchapter “Using a Mutable Object as the Default Value of a Function Parameter”, found in the chapter “Functions That Can Remember” of the e-book Python Knowledge Building Step by Step.