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. Thex
argument receives the accumulated value andy
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()
.