Python Reduce

Python Reduce Function: Should You Use It or Not?

The Python reduce function is one of those topics you encounter the more you code in Python. It might sound complex, is that really the case?

The Python reduce function is used to apply a given function to the items of an iterable and it returns a single value. The function is applied to two items at the time from left to right until all the items of the iterable are processed.

We will work with a few examples that use the reduce function to make sure you understand how to use it.

Let’s start coding!

How Does Reduce Work in Python?

The Python reduce() function is part of the functools module and takes as arguments a function and an iterable.

functools.reduce(functioniterable)

The reduce operation doesn’t return multiple values, it just returns a single value.

The reduce function reduces an iterable to a single value.

Here are the steps reduce follows to generate its result:

  1. It applies the function to the first two elements of the iterable and it generates a result.
  2. The function is then applied to the result generated at step 1 and to the next item in the iterable.
  3. The process continues until all the items in the iterable are processed.
  4. The final result is returned by the reduce function.

Let’s define a custom function that calculates the sum of two numbers:

def sum(x,y):
    return x+y 

Then we import the reduce function from the functools module, apply the function to a list of numbers and print the result.

from functools import reduce

def calculate_sum(x,y):
    return x+y

numbers = [1, 3, 5, 7]
result = reduce(calculate_sum, numbers)
print("The result is {}".format(result)) 

Note: by using from … import we only import the reduce function from functools instead of importing the entire functools module.

When you execute this code you get the following result (I’m using Python 3):

$ python reduce.py
The result is 16

So, given a list of numbers we get back as result the total sum of the numbers.

To make sure it’s clear how reduce behaves below you can see how the sum is calculated:

(((1 + 3) + 5) + 7) => 16

How Can You Use Reduce With a Lambda?

In the previous section we have defined a function that calculates the sum of two numbers and then we have passed that function to the reduce function.

We can also replace the calculate_sum function with a lambda function.

lambda x, y : x + y

If we pass two numbers to this lambda in the Python shell we get back their sum:

>>> (lambda x, y: x + y)(1, 2)
3 

And now let’s pass this lambda as first parameter of the reduce function…

from functools import reduce 

numbers = [1, 3, 5, 7]
result = reduce(lambda x, y: x + y, numbers)
print("The result is {}".format(result)) 

The output returned by reduce is:

$ python reduce.py 
The result is 16

Exactly the same output we have got back when using the custom calculate_sum function.

Applying Python Reduce to an Empty List

Let’s find out what result we get when we pass an empty list to the reduce function.

As first argument we will keep the lambda used in the previous section.

result = reduce(lambda x, y: x + y, [])
print("The result is {}".format(result)) 

[output]
Traceback (most recent call last):
  File "reduce.py", line 3, in <module>
    result = reduce(lambda x, y: x + y, [])
TypeError: reduce() of empty sequence with no initial value 

We get back a TypeError exception that complains about the fact that there is no initial value.

What does it mean exactly?

If you look at the Python documentation for the reduce function you will see that this function also supports an optional third argument, an initializer.

functools.reduce(functioniterable[, initializer])

The initilizer, if present, is placed before the items of the iterable in the calculation and it’s used as default value in case the iterable is empty.

Update the code to pass an initilizer equal to 10.

result = reduce(lambda x, y: x + y, [], 10)
print("The result is {}".format(result))

[output]
The result is 10 

This time the reduce function does not raise a TypeError exception. Instead it returns the value of the initializer.

Before continuing verify the output from the reduce function when the initializer is present and the list is not empty:

result = reduce(lambda x, y: x + y, [1, 2], 10) 

What do you get back? Is the result what you expected?

Why Are You Getting the Python Error “reduce” is Not Defined?

If you are running a program that calls the reduce() function without importing it from functools you will get the following NameError exception:

Traceback (most recent call last):
  File "reduce.py", line 7, in <module>
    result = reduce(sum, numbers)
NameError: name 'reduce' is not defined 

The fix is simple, just add an import statement at the top of your Python program as shown before:

from functools import reduce

Difference Between Map and Reduce

Another function that gets often mentioned together with reduce is the map function.

The main difference between map and reduce is that map() is applied to every item of an iterable one at the time and it returns an iterator.

Let’s see what happens if we pass the lambda defined before to the map function.

>>> result = map(lambda x, y: x + y, [1, 2])
>>> print(list(result))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'y' 

The Python interpreter raises a TypeError because the map function passes only one value to the lambda function.

Update the lambda function by removing y and by returning x multiplied by 2:

>>> result = map(lambda x: 2*x, [1, 2])
>>> print(type(result))
<class 'map'> 
>>> print(list(result))
[2, 4] 

This time the map function works as expected.

Notice that we have used the list() function to convert the map object returned by the map function into a list.

Reduce vs Python For Loop

I wonder how we can use a Python for loop to write a program that returns the same result as the reduce function.

We set the value of result to 0 and then add each item in the list to it.

numbers = [1, 3, 5, 7] 
result = 0

for number in numbers:
    result += number

print("The result is {}".format(result)) 

As you can see we need few lines of code to do what reduce does in a single line.

Reduce vs Python List Comprehension

There is a conceptual difference between the reduce function and a list comprehension.

Reduce starts from a Python list and returns a single value while a list comprehension applied to a list returns another list.

But there are some scenarios in which you can use a list comprehension and the reduce function in a similar way, for example to flatten a list of lists.

Given the following list of lists:

>>> numbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 

I want to use reduce to flatten it.

>>> from functools import reduce
>>> result = reduce(lambda x, y: x + y, numbers)
>>> print(result)
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

As you can see we have converted the list of lists into a simple list that contains all the numbers.

Now let’s write the code to do this with a list comprehension.

>>> [item for number_group in numbers for item in number_group]
[1, 2, 3, 4, 5, 6, 7, 8, 9] 

The result is the same.

Which approach do you prefer?

Reduce vs Itertools.accumulate

The itertools module implements a function called accumulate.

How does it compare to the reduce function?

Firstly its syntax is different:

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

It accepts an iterable as first argument and an optional function as second argument.

Let’s apply it to our original list of numbers to see what happens…

>>> from itertools import accumulate
>>> numbers = [1, 3, 5, 7]
>>> print(type(accumulate(numbers)))
<class 'itertools.accumulate'>
>>> print(list(accumulate(numbers)))
[1, 4, 9, 16] 

The accumulate function creates an iterator that returns accumulated sums.

So the behaviour is different from the reduce function that just returns a single value.

Conclusion

You have reached the end of this tutorial and by now you have all the knowledge you need to use the Python reduce function.

You know how to use it by passing a custom function or a lambda function to it.

We have also looked at how reduce compares to map and how you can write code that implements a logic similar to reduce using a for loop or a list comprehension (only in some cases).

Share knowledge with your friends!

Leave a Reply

Your email address will not be published. Required fields are marked *