Python 3x Pandas Django

Functional Programming in Python


Introduction

Functional programming is all about separation of concerns which object-oriented programming does this well right? yes. it's all about packing our code into separate chunks so that everything well-organized in each part of our code and each part is organize in a way that makes sense based on the functionality so when we say separation of concerns, we mean, each part concerned itself with one thing that is good at.

We remember in the OOPs section, how we had classes to divide up attributes and methods. Functional programming has this idea as well of separating the concerns but also separate data & functions that's how functional programmer view the world instead of combining methods and attributes we separate them up because they are two separate things there is data and this data gets interacted and acted upon but we are not going to combine both data and functions as one piece in an object that we saw in the object-oriented programming section.

What is functional programming?

In general, functional languages have an emphasis on simplicity and data & function are concerned because in the most functional programming paradigm we don't have an idea classes and objects instead functions operates on well-defined data structures like list & dictionaries that we saw, rather than belonging that data structure to an object but at the end of the day the goal of functional programming paradigm is same as the object-oriented paradigm. It is an idea of making our code,

Clean & understandable

Easy to extend

Easy to maintain

Dry & memory efficient

Functional programming typically plays a fairly small role in Python code. But it’s good to be familiar with it. Sometimes, You may even find situations where it’s advantageous to use Python’s functional programming capabilities in your own code.

Pure Function

1. The pure function should do not change the value of the input or any data that exists outside the function's scope.

2. The pure function should do not change the state of the program. Given the same input, a pure function will always produce the same output.

Example:

def multiply_by_2(input_numbers):
    new_numbers = []
    for n in input_numbers:
        new_numbers.append(n * 2)
    return new_numbers

input_numbers = [2, 4, 6]
output_numbers = multiply_by_2(input_numbers)

print(input_numbers)   #Output: [2, 4, 6]
print(output_numbers)  #Output: [4, 8, 12]

In the above example, multiply_by_2 function do not change any data outside the function's scope and the state of the program.

Let's check some important functions in functional programming paradigm,

Map()

Syntax:

map(function,  *iterables)

function : It is a function to which map passes each element of given iterable.

*iterables : It is a iterable which is to be mapped

Example 1:

input_numbers = [2, 4, 6]

def multiply_by_2(item):
    return item * item

output_numbers = list(map(multiply_by_2, input_numbers))
print(output_numbers)      #Output: [4, 16, 36]

Above example 1 is a simplified version of code from the example in pure function.

Example 2:

names = ['Johnson', 'Michael', 'Lisa']

lower_names = list(map(str.lower, names))
print(lower_names)          #Output: ['johnson', 'michael', 'lisa']

Filter()

Use filter to extract values that meet certain conditions in an iterable.

Syntax:

filter(function,  *iterables)

function : It is a function that tests if each element of a sequence true or not.

*iterables : It is a iterable which is to be filtered.

Example 1:

input_numbers = [2, 4, 1, 6, 5]

def only_odd(item):
    return item % 2 != 0

output_numbers = list(filter(only_odd, input_numbers))
print(output_numbers)      #Output: [1, 5]

In the above example, filter function checks whether the element is odd or even.

Example 2:

input_numbers = [2, 'b', 1, 'a', 5]

def num_type(item):
    return type(item) == int

output_numbers = list(filter(num_type, input_numbers))
print(output_numbers)      #Output: [2, 1, 5]

zip()

With zip you can combine multiple iterables into one data structure.

Syntax:

zip(*iterables)

*iterables : It is a iterable which is to be zipped.

Example 1:

input_numbers1 = [2, 4, 1, 6, 5]
input_numbers2 = [2, 4, 1, 6, 5]

print(list(zip(input_numbers1, input_numbers2)))

#Output: [(2, 2), (4, 4), (1, 1), (6, 6), (5, 5)]

Example 2:

input_numbers1 = [2, 4, 1, 6, 5]
input_numbers2 = [2, 4, 1]

print(list(zip(input_numbers1, input_numbers2)))

#Output: [(2, 2), (4, 4), (1, 1)]

In the above example, zip takes the minimum item of list and zip it.

reduce()

It's not a built-in function. It's a function from a package functools.

reduce applies a function of two arguments cumulatively to the elements of an iterable, optionally starting with an initial argument. It has the following syntax:

Syntax:

reduce(function, iterable[, initial])

Example 1:

from functools import reduce

input_list = [2, 4, 1, 6, 5]

def accumulator(initial, item ):
    return initial + item

print(reduce(accumulator,input_list, 0))  #Output: 18

Example 2:

from functools import reduce

input_list = [2, 4, 1, 6, 5]

def accumulator(initial, item ):
    return initial + item

print(reduce(accumulator,input_list, 2)) #Output: 20

lambda Expressions

A lambda function is a small anonymous function. It is a throw-away functions, i.e. they are just needed where they have been created.

A lambda functions are mainly used in combination with the functions filter(), map() and reduce().

Lambda with map, filter, reduce

from functools import reduce
my_list = [1, 2, 3]

#Square the item
print(list(map(lambda item: item * 2, my_list)))           # [2, 4, 6]

#Filter the item by odd numbers
print(list(filter(lambda item: item % 2 != 0, my_list)))   # [1, 3]

#Filter the item by odd numbers
print(reduce(lambda acc, item : acc + item, my_list))      # 6

If you have any doubts or queries related to this chapter, get them clarified from our Python Team experts on ibmmainframer Community!