The Python reduce() Function

Introduction

The Python reduce() function aggregates all items in an iterable. It works iteratively by combining each item in succession with the previously calculated aggregate value. You can think of reduce() as a function that reduces a collection of values into a single value, and that’s where it gets its name. For example, a list of numbers can be reduced to a single multiplicative product by successively multiplying each item in the list with the previously calculated product.

The reduce() function is part of the functools module, and must be imported from there using a from-import statement. Its utility lies in simplifying code that involves iterative aggregation, offering a concise alternative to traditional loops.

Basic Python reduce() Function Syntax

The basic syntax of the reduce() function involves passing a function (which can also be a lambda expression) and an iterable as arguments. The function should take two arguments, perform the desired operation on those and return its result. The reduce() function successively applies this function to the elements of the iterable, accumulating a single result.

The following is the reduce() syntax:

from functools import reduce

reduce(function, iterable[, initializer])
  • function: The function to apply cumulatively to the items of the iterable.
  • iterable: The iterable whose elements will be aggregated.
  • initializer: An optional argument providing the initial value for the aggregation. If not provided, the first element of the iterable is used. (The square brackets are not part of the syntax, and they are only there to indicate the optional nature of the argument.)

The following example shows how to use reduce() to find the multiplicative product of all numbers in a list.

Example:

from functools import reduce

numbers = [2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)

Output:

120

In this example:

  • lambda x, y: x * y: The lambda function that defines the multiplication operation between two elements. The x argument receives the accumulated value and y the next item from the iterator.
  • numbers: The original list of numbers.
  • reduce(lambda x, y: x * y, numbers): Applies the multiplication function cumulatively to all elements in the list.

Unlike map(), the result of reduce() is not an iterable but a single value that is the cumulative operation on all elements. This is why there is no need to convert the result to a list or other object before printing.

The Accumulation Process

Let’s take a closer look at how the accumulation process of the reduce() function works by replacing the lambda expression in the last example with an equivalent function that also prints out its arguments

Example:

from functools import reduce

def mult(x, y):
    print(f"Accumulated value: {x}  Next value: {y}")
    return x * y
    
numbers = [2, 3, 4, 5]
product = reduce(mult, numbers)
print(f"Final product: {product}")

Output:

Accumulated value: 2  Next value: 3
Accumulated value: 6  Next value: 4
Accumulated value: 24  Next value: 5
Final product: 120

As you can see in line 1 of the output, the initial accumulated value is 2. Since map() was not called with an initializer argument, it defaults to the first item in the numbers list, 2. Consequently, in the absence of an initializer, the first call to the function is made with the first two items of the iterator as arguments.

After the first call, the accumulated value is the returned result of the previous function call. For example, the accumulated value of the second call (line 2) is 6. It is the product of 2 and 3, which are the function arguments in the first call (line 1). And the accumulated value of the third call (line 3) is 24. It is the product of 6 and 4, which are the function arguments in the second call (line 2).

Using reduce() with an Initializer

The initializer argument is optional, but it can be useful in certain scenarios. If provided, it becomes the initial value for the cumulative operation. This can be particularly handy when working with an iterable that might be empty.

Example:

from functools import reduce

numbers = []
result = reduce(lambda x, y: x + y, numbers, 0)
print(result)

Output:

0

In the example above, the reduce() function is used to sum numbers in a list. It starts with an initial value of 0, which is given as the third argument. In case the numbers list is empty, the result would be 0, which makes sense for a sum.

Python will generate a TypeError if reduce() is given an empty iterable with no initial value:

Example:

from functools import reduce

numbers = []
result = reduce(lambda x, y: x + y, numbers)
print(result)

Output:

...
TypeError: reduce() of empty sequence with no initial value

Non-Commutative Operations

In a commutative operation, the order of the items operated on does not matter. Both addition and multiplication are examples of commutative operations. Therefore, in the previous examples, it wouldn’t make a difference if we flip the accumulated and next value arguments in the function. For instance, lambda x, y: x * y produces the same results as lambda x, y: y * x.

However, this is not always the case. For some operations, the order does matter. In those instances, you must carefully distinguish between the accumulated value, which is the first argument, and the next iterator value, which is the second.

The next example shows a use case where the order of the arguments is important. It uses reduce() to concatenate a list of words into a single string in reverse order.

Example:

from functools import reduce

words = ["hello", "world", "python", "programming"]
result = reduce(lambda x, y: y + " " + x, words)
print(f"The concatenated string in reverse order is: {result}")

Output:

The concatenated string in reverse order is: programming python world hello

In the example above, the lambda function lambda x, y: y + " " + x concatenates two words in reverse order. The accumulated value x stores the previously concatenated words, and the next value y is the next word from the iterator. In this operation, the order does matter. If we switch x and y by placing x first and y second, the words would not be reversed. The order of the arguments in the lambda function is crucial for building the string in reverse order.

Built-In Functions

The reduce() function is very powerful but not always necessary because Python does provide built-in implementations for some simple use-cases. For example, the multiply() and sum() functions will multiply and sum up iterables. There is no need to use reduce() implementations of those as you’ve seen in this lesson. They were merely convenient illustrative examples. Additionally, the join() function can join words into a single string, but it does not reverse them.

Summary & Reference for the Python reduce() Function

The reduce() function aggregates successive elements of an iterable using a function. It returns a single value representing the cumulative result of the operation.


The reduce() function is in the functools module and must be imported with an import statement such as from functools import reduce.


The basic reduce() function syntax involves passing a function, an iterable and an optional initial value: reduce(function, iterable[, initializer])

numbers = [2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)  # product --> 120

The initializer argument is optional and provides an initial value for the cumulative operation. The initial value ensures that the reduce() function will work in case the iterable it’s given is empty.

numbers = []
result = reduce(lambda x, y: x + y, numbers, 0)  # result --> 0

The function passed to reduce() accumulates values successively, with each iteration combining the result of the previous operation and the next element from the iterable. The first argument to the function is the accumulated value, and the second is the next value from the iterable. It returns the result of the operation.

def mult(x, y):
    print(f"Accumulated value: {x}  Next value: {y}")
    return x * y

The reduce() function is very powerful but not always necessary because Python provides built-in functions for some simple use-cases, such as sum(), multiply() and join().