Functional Programming with Python

Knoldus Blog Audio
Reading Time: 4 minutes

What is Functional Programming?

“Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data”. In other words, functional programming (FP) promotes code with no side effects and immutable variables. It is a declarative type of programming style. Its main focus is on “what to solve” in contrast to an imperative style where the main focus is “how to solve”.

Functional Programming Concepts

Functional Programming

1. Pure Functions

Pure functions have two properties:

a) They create no side effects.
b) They always produce the same output if given the same input. This means that the function cannot depend on any mutable state.

Side effects can be caused due to the following reasons :-

  • Changing data that exists outside the function’s scope.
  • Changing the value of arguments in the function.
  • Throwing an exception or halting with an error.
  • Printing to the console or reading user input.
  • Reading from or writing to a file.

Let’s look at an example of Impure Function.

num2 = 3
# Adds two numbers, but uses the global `num2` variable.
def add_impure(num1):
    return num1 + num2

print(add_impure(5))

Let’s now look at the pure function of adding two numbers.

# Adds two numbers, using the arguments passed to the function.
def add_pure(num1, num2):
    return num1 + num2

print(add_pure(5, 3))

2. Immutability

In functional programming, we can’t change the value of a variable after it has been initialized. This means if we need to change values in a list, we need to create a new list with updated values rather than changing the existing one. In FP, the immutable nature of variables helps by preserving the state throughout the execution of a program. In Python, data types such as int, float, complex, string, tuple, frozen set, bytes are immutable.

#  id() function returns a unique id for the specified object.
num1 = 21
print(id(num1))

num1 = 12
print(id(num1))

-----------------------------------------------------------------------------------
OUTPUT:

1705703664
1705703520
tuple1 = (0, 1, 2, 3) 
tuple1[0] = 4
print(tuple1)

-----------------------------------------------------------------------------------
OUTPUT:

Traceback (most recent call last):
  File "D:/LeetCode/FP.py", line 2, in <module>
    tuple1[0] = 4
TypeError: 'tuple' object does not support item assignment

Process finished with exit code 1

3. Recursion

In FP, we avoid if-else statements or loops as it creates different outputs on each execution. Functional programs use recursion in place of loops for all the iteration tasks. Recursion is a function which calls itself again and again until an exit condition is met.

# returns factorial of a number
def factorial(num):

    if num == 1:
        return 1
    else:
        return num * factorial(num - 1)

  
print(factorial(5))

4. Functions are First-Class and can be Higher-Order

In FP, functions are treated as a data type and can be used like any other value. You can assign functions to variables, store them in data structures, pass them as arguments, or use them in control structures. For example, we can populate an array with functions, pass them as parameters, or store them in variables.

def cube(number):
    return number * number * number

my_cube = cube  # Assigning function as an object
print(my_cube(5))

numbers = [21, my_cube(5), 12]  # Storing function in list
print(numbers)

# Returning function from another function
def display_numbers():
    return my_cube(5)

print(display_numbers())    

A function is called a higher-order function if it contains other functions as a parameter or returns a function as an output.

Map, Filter and Reduce are some of the built-in higher order functions in Python.

Higher Order Function — Map

The map function takes two arguments. The first argument is a function and the second is iterable. It then applies the passed function to each item in the iterable.

num = [1, 2, 3, 4, 5]
# returns square of each element in the list.
square = list(map(lambda number: number ** 2, num))
print(square)

-------------------------------------------------------------------------------------------
OUTPUT:

[1, 4, 9, 16, 25]
Higher Order Function — Filter

The filter function allows you to easily extract matching records from a larger set of data based on the criteria you provide. It takes two arguments. The first argument is a function and the second is iterable. The filter function returns a sequence from those elements of iterable for which function returns true.

num = [1, 2, 3, 4, 5]
# returns even numbers from list.
even = list(filter(lambda number: number % 2 == 0, num))
print(even)

-------------------------------------------------------------------------------------------
OUTPUT:

[2, 4]
Higher Order Function — Reduce

The reduce function accepts a function and a sequence and returns a single value which is calculated as follows:

  1. Initially, the function is called with the first two items from the sequence and the result is returned.
  2. The function is then called again with the result obtained in step 1 and the next value in the sequence. This process is repeated until each item in the sequence is iterated.
from functools import reduce

def add(num1, num2):
    return num1 + num2

print(reduce(add, [1, 2, 3, 4]))

-------------------------------------------------------------------------------------------
OUTPUT:

10

Advantages of Functional Programming

  • Pure functions always produce the same output and have no external values affecting the end result. Due to this programs are easier to test and debug.
  • We can write efficient parallel or concurrent programs because they run independently without changing state.
  • It supports the concept of lazy evaluation, which means we evaluate the value and store them only when it is needed.
  • Unlike OOP, FP supports better Encapsulation with Pure Functions.
  • As FP programs are made up of pure functions, we can reuse them very easily.

Disadvantages of Functional Programming

  • Immutable values when combined with recursion might reduce the performance.
  • In some cases, writing pure functions may reduce the readability of the code.
  • Functional Programming doesn’t have a state. They always create new objects to perform actions instead of making changes to existing objects. Due to this, FP Applications take a lot of memory.
  • Writing pure functions is easy, but combining them into a complete application is where things get hard.

Conclusion

This blog provides a glimpse of what functional programming does and should give you some context on how they are beneficial. Python allows us to code in a functional, declarative style. It even has support for many common functional features like lambda, map(), filter(), and reduce() which can help you write concise, high-level, parallelizable code.