Elements of Functional Programming in Python
Learn how to use the lambda, map, filter, and reduce functions in Python to transform data structures.
“Object-oriented programming makes code understandable by encapsulating moving parts. Functional programming makes code understandable by minimizing moving parts.” : Michael Feathers
There are multiple programming languages in the world, and so are the categories in which they can be classified. A programming paradigm is one such way which tries to classify programming languages based on their features or coding style. A programming paradigm is essentially a style or a way of programming.
Most of the times, we understand Python as an object-oriented language, where we model our data in the form of classes, objects, and methods. However, there also exist several alternatives to OOP, and functional programming is one of them.
Here are some of the conventional programming paradigms prevalent in the industry:
As per Wikipedia, Functional programming is a programming paradigm, a style of building the structure and elements of computer programs, that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data.
The above definition might sound confusing at first, but it essentially tries to put forward the following aspects:
- FP relies on functions, and everything is done using functions. Moreover, FP focuses on defining what to do, instead of performing some action. The functions of this paradigm are treated as first-class functions. This means functions are treated like any other objects, and we can assign them to variables or pass them into other functions.
- The data used in functional programming must be immutable, i.e. should never change. This means if we need to modify a data in a list, we need to create a new list with updated values rather than manipulating the existing one.
- The programs written in FP should be stateless. A stateless function has no knowledge about its past. Functional programs should carry out every task as if they are performing it for the first time. Simply put, the functions are only dependent on the data passed to them as arguments and never on the outside data.
- Laziness is another property of FP wherein we don’t compute things that we don’t have to. Work is only done on demand.
If this makes sense now, here is a nice comparison chart between the OOP and FP which will make things even more apparent.
Python provides features like lambda, filter, map, and reduce that can easily demonstrate the concept of Functional Programming. All the codes used in this article can be accessed from the associated Github Repository or can be viewed on my_binder by clicking the image below.
The Lambda Expression
Lambda expressions — also known as “anonymous functions” — allow us to create and use a function in a single line. They are useful when we need a short function that will be used only once. They are mostly used in conjunction with the map, filter and the sort methods, which we will see later in the article.
Let’s write a function in Python, that computes the value of
5x + 2. The standard approach would be to define a function.
Now let’s compute the same expression using Lambda functions. To create a lambda expression, we type in the keyword lambda, followed by the inputs. Next, we enter a colon, followed by the expression that will be the return value.
This lambda function will take the input x and return
5x + 2, just like the earlier function
f. There is a problem, however. Lambda is not the name of the function. It is a Python keyword that says that what follows is an anonymous function. So how do we use it? One way is to give it a name.
Let us call this lambda expression
g. Now, we can use this like any other function.
Lambda expression with multiple inputs.
The examples below show how lambda functions can be used with or without input values.
Lambda expression without inputs.
Now, let’s look at a common use of Lambda function where we do not assign it a name. Let’s say we have a list of the first seven U.S Presidents and we’d like to sort this list by their last name. We shall create a Lambda function that extracts the last name, and uses that as the sorting value.
The map, filter, and reduce functions simplify the job of working with lists. When used along with lambda expressions they help to make our lives easier by accomplishing a lot in a single line of code.
The Map Function
The map function applies a function to every item of iterable, yielding the results. When used with lists,
Map transforms a given list into a new list by applying the function to all the items in an input_list.
Suppose we have a function that computes the volume of a cube, given the value of its edge(a).
def volume(a): """volumne of a cube with edge 'a'""" return a**3
What if we need to compute the volumes for many different cubes with different edge lengths?
# Edge length in cm edges = [1,2,3,4,5]
There are two ways to do this — one by using the direct method and the other by using the map function.
Now let’s see how we can accomplish this task using a single line of code with the map function.
The map function takes in two arguments. The first is a function, and the second is our list, tuple, or any other iterable object. Here, the map function applies the volume function to each element in the list.
An important thing to note here is that the output of the map function is not a list but a map object, which is an iterator over the results. We can, however, turn this into a list by passing the map to the list constructor.
Let’s now see an example which demonstrates the use of
lambda function with the
map function. We have a list of tuples containing name and heights for five people. Each of the height is in centimeters, and we need to convert it into feet.
We will first write a converter function using a lambda expression which will accept a tuple as the input and will also return a tuple.
The Filter Function
The ‘filter’ function constructs an iterator from those elements of iterable for which function returns true. This means filter function is used to select certain pieces of data from a list, tuple, or other collection of data, hence the name.
Let’s see an example where we want to get the list of all numbers that are greater than 5, from a given input list.
We first create a lambda function that tests the input to see if it is above 5 or not. Next, we pass in the list of data. The filter function will only return the data for which the function is true. Once again, the return value is not a list, but a filter object. This object has to be passed to a list constructor to get the output.
An interesting use case of the ‘filter’ function arises when the data consists of missing values. Here is a list containing some of the countries in Asia. Notice numerous strings are empty. We’ll use the filter function to remove these missing values. We’ll pass none as the first argument, and the second argument is the list of data as before.
This filters out all values that are treated as false in a boolean setting.
The Reduce Function
The ‘reduce’ function is a bit unusual, and, as of Python 3, it is no longer a built-in function. Instead, it has been moved to the
functools module. The ‘reduce’ function transforms a given list into a single value by applying a function cumulatively to the items of sequence, from left to right,
where reduce continually applies the function
func() to the sequence seq and returns a single value.
Let’s illustrate the working of the
reduce function with the help of a simple example that computes the product of a list of integers.
The following diagram shows the intermediate steps of the calculation:
However, Guido van Rossum, the creator of Python, had to say this about the ‘reduce’ function:
Use functools.reduce if you really need it; however, 99% of the time an explicit for loop is more readable.
The above program can also be written with an explicit for loop:
The ‘reduce’ function can determine the maximum of a list containing integers in a single line of code. There does exist a built-in function called
max() in Python which is generally used for this purpose as
List Comprehensions: Alternative to map, filter and reduce
List comprehension is a way to define and create lists in Python. In most cases, list comprehensions let us create lists in a single line of code without worrying about initializing lists or setting up loops.
It is also a substitute for the lambda function as well as the functions map(), filter() and reduce(). Some people find it a more pythonic way of writing functions and find it easier to understand.
Let’s try and replicate the examples used in the above sections with
- List Comprehensions vs. Map function
We used map function in conjunction with the lambda function to convert a list of heights from cm to feet. Let’s use list comprehensions to achieve the same results.
- List Comprehensions vs. Filter function
We used the filter function to remove the missing values from a list of Asian countries. Let’s use list comprehensions to get the same results.
- List Comprehensions vs. Reduce function
Similarly, we can determine the maximum of a list containing integers quickly with list comprehension instead of using lambda and reduce.
We have used a generator expression above which is similar to list comprehension but with round brackets instead of the square one.
List comprehensions are a diverse topic and require an article of their own. Keeping this in mind, here is an article that I wrote that covers not only list comprehensions but even dictionary, set, and generator comprehensions in python.
The map, filter, and reduce functions significantly simplify the process of working with lists and other iterable collections of data. Some people have reservations about using them, especially since list comprehensions appear to be more friendly, yet their usefulness cannot be ignored.