How to Use itertools.accumulate in Python

Python's itertools.accumulate() function is a powerful, memory-efficient tool designed to calculate running totals and intermediate reductions over iterable data. While traditional reduction tools like functools.reduce() return only the final accumulated value, accumulate() returns an iterator yielding every progressive step in the calculation. This article explains how itertools.accumulate() operates under the hood, how to implement basic running sums, and how to supply custom binary functions to compute cumulative products, running maximums, and specialized stateful reductions.

Syntax and Basic Operation

The function is imported from the standard library's itertools module. Its basic signature is:

itertools.accumulate(iterable[, func, *, initial=None])

By default, if no custom function is provided, accumulate() uses standard addition (+). It reads the first item from the input iterable, yields it, and then continuously adds subsequent items to the running total, yielding each intermediate result.

import itertools

numbers = [1, 2, 3, 4, 5]
running_sums = list(itertools.accumulate(numbers))
print(running_sums)
# Output: [1, 3, 6, 10, 15]

Because accumulate() returns an iterator, it computes values lazily. This ensures \(O(1)\) auxiliary memory overhead, making it well-suited for processing massive datasets or infinite streams.

Applying Custom Reductions

The func parameter allows you to replace default addition with any callable that accepts two arguments: the current accumulated value and the next item from the iterable.

1. Running Product

To calculate a running product (cumulative factorial or compounding growth), pass operator.mul:

import itertools
import operator

numbers = [1, 2, 3, 4, 5]
running_product = list(itertools.accumulate(numbers, operator.mul))
print(running_product)
# Output: [1, 2, 6, 24, 120]

2. Running Minimum or Maximum

You can track the progressive extreme values across an iterable by passing Python’s built-in min or max functions:

import itertools

temperatures = [72, 75, 71, 78, 69, 80]
running_max = list(itertools.accumulate(temperatures, max))
print(running_max)
# Output: [72, 75, 75, 78, 78, 80]

3. Custom Logic Using Callables

Custom lambda functions or defined functions can implement domain-specific logic, such as compound interest or a bounded counter:

import itertools

# Simulate bank balance with a fixed 10% interest added to each deposit
deposits = [100, 200, 50]
compound = list(itertools.accumulate(deposits, lambda bal, dep: (bal * 1.10) + dep))
print(compound)
# Output: [100, 310.0, 391.0]

Using the initial Parameter

Python 3.8 introduced the initial keyword argument. When provided, the iteration begins by yielding the initial value, which is then used as the starting accumulation state for the first element of the input iterable.

import itertools

data = [10, 20, 30]
# Start accumulation from a base value of 100
result = list(itertools.accumulate(data, initial=100))
print(result)
# Output: [100, 110, 130, 160]

If the input iterable is empty and initial is specified, accumulate() yields only the initial value. If initial is not set and the input iterable is empty, it returns an empty iterator.