reduce() functions bring a bit of functional programming to Python. All three of these are convenience functions that can be replaced with List Comprehensions or loops, but provide a more elegant and short-hand approach to some problems.
Before continuing, we'll go over a few things you should be familiar with before reading about the aforementioned methods:
What is an anonymous function/method or lambda?
An anonymous method is a method without a name, i.e. not bound to an identifier like when we define a method using
Note: Though most people use the terms "anonymous function" and "lambda function" interchangeably - they're not the same. This mistake happens because in most programming languages lambdas are anonymous and all anonymous functions are lambdas. This is also the case in Python. Thus, we won't go into this distinction further in this article.
What is the syntax of a lambda function (or lambda operator)?
lambda arguments: expression
Think of lambdas as one-line methods without a name. They work practically the same as any other method in Python, for example:
def add(x,y): return x + y
Can be translated to:
lambda x, y: x + y
Lambdas differ from normal Python methods because they can have only one expression, can't contain any statements and their return type is a
function object. So the line of code above doesn't exactly return the value
x + y but the function that calculates
x + y.
Why are lambdas relevant to
All three of these methods expect a
function object as the first argument. This
function object can be a pre-defined method with a name (like
Though, more often than not, functions passed to
reduce() are the ones you'd use only once, so there's often no point in defining a referenceable function.
To avoid defining a new function for your different
reduce() needs - a more elegant solution would be to use a short, disposable, anonymous function that you will only use once and never again - a lambda.
The map() Function
map() function iterates through all items in the given iterable and executes the
function we passed as an argument on each of them.
The syntax is:
We can pass as many iterable objects as we want after passing the
function we want to use:
# Without using lambdas def starts_with_A(s): return s == "A" fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"] map_object = map(starts_with_A, fruit) print(list(map_object))
This code will result in:
[True, False, False, True, False]
As we can see, we ended up with a new list where the function
starts_with_A() was evaluated for each of the elements in the list
fruit. The results of this function were added to the list sequentially.
A prettier way to do this exact same thing is by using lambdas:
fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"] map_object = map(lambda s: s == "A", fruit) print(list(map_object))
We get the same output:
[True, False, False, True, False]
Note: You may have noticed that we've cast
map_object to a list to print each element's value. We did this because calling
print() on a list will print the actual values of the elements. Calling
map_object would print the memory addresses of the values instead.
map() function returns the
map_object type, which is an iterable and we could have printed the results like this as well:
for value in map_object: print(value)
If you'd like the
map() function to return a list instead, you can just cast it when calling the function:
result_list = list(map(lambda s: s == "A", fruit))
The filter() Function
filter() takes a
function object and an iterable and creates a new list.
As the name suggests,
filter() forms a new list that contains only elements that satisfy a certain condition, i.e. the
function we passed returns
The syntax is:
Using the previous example, we can see that the new list will only contain elements for which the
starts_with_A() function returns
# Without using lambdas def starts_with_A(s): return s == "A" fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"] filter_object = filter(starts_with_A, fruit) print(list(filter_object))
Running this code will result in a shorter list:
Or, rewritten using a lambda:
fruit = ["Apple", "Banana", "Pear", "Apricot", "Orange"] filter_object = filter(lambda s: s == "A", fruit) print(list(filter_object))
Printing gives us the same output:
The reduce() Function
reduce() works differently than
filter(). It does not return a new list based on the
function and iterable we've passed. Instead, it returns a single value.
Also, in Python 3
reduce() isn't a built-in function anymore, and it can be found in the
The syntax is:
reduce(function, sequence[, initial])
reduce() works by calling the
function we passed for the first two items in the sequence. The result returned by the
function is used in another call to
function alongside with the next (third in this case), element.
This process repeats until we've gone through all the elements in the sequence.
The optional argument
initial is used, when present, at the beginning of this "loop" with the first element in the first call to
function. In a way, the
initial element is the 0th element, before the first one, when provided.
reduce() is a bit harder to understand than
filter(), so let's look at a step by step example:
We start with a list
[2, 4, 7, 3]and pass the
add(x, y)function to
reduce()alongside this list, without an
add(2, 4), and
add(6, 7)(result of the previous call to
add()and the next element in the list as parameters), and
add(13, 3), and
Since no more elements are left in the sequence,
The only difference, if we had given an
initial value would have been an additional step - 1.5. where
reduce() would call
add(initial, 2) and use that return value in step 2.
Let's go ahead and use the
from functools import reduce def add(x, y): return x + y list = [2, 4, 7, 3] print(reduce(add, list))
Running this code would yield:
Again, this could be written using lambdas:
from functools import reduce list = [2, 4, 7, 3] print(reduce(lambda x, y: x + y, list)) print("With an initial value: " + str(reduce(lambda x, y: x + y, list, 10)))
And the code would result in:
16 With an initial value: 26
As mentioned previously, these functions are convenience functions. They are there so you can avoid writing more cumbersome code, but avoid using both them and lambda expressions too much.
Don't force these tools because "you can", as it can often lead to illegible code that's hard to maintain. Use them only when it's absolutely clear what's going on as soon as you look at the function or lambda expression.
If you catch yourself struggling to fit the necessary logic into one
map() function, or one lambda expression, it's much better to just write a slightly longer for-loop/defined method and avoid unnecessary confusion later.