Young hacker smiling

We hack your software

zero false positives

Expert intelligence + effective automation

Swiss army knives with Python logo

Road to Functional Python

Functional coding in Python
An introduction to the essential aspects of functional programming in Python. Benefits of functional Python. How to start the migration to functional paradigm. What are the most used functional libraries. A reflexion on why focus our efforts in create functional code.

Probably there is nothing better for the spirit than having a hobby that we are passionate about, that makes us feel in love all the time and makes us want to return to it. Better, the results obtained while practicing your hobby, without any intention, becomes a global phenomenon, used by large and small companies becoming in the source of income for many. This is how Python came about, a project initially as a hobby, but over the years has earned a place among the most widely used languages, not only because is easy to use but for its versatility. Python has been able to adapt to the frenetic changes in technology and has been a strong contender in many fields. Today it wants to change again, it wants to evolve before the same evolution arrives. Today it wants to be functional!

Before putting on shoes

Before starting, I must warn you that this trip is not for beginners. You will not be able to face functional Python and survive if you do not know the basic Python. If this is your case, do not feel bad, you can not walk if you don’t even know how to stand, so go and scrape your knees in a Python playground. Also, get some context by reading Why we go functional?, which is the prequel of this article. When you think you’re ready, come back to the road, always be waiting for you.

Prepare your backpack

Of course we need supplies for the road. But surprise! If you already have Python then you are ready to start walking. One of the most important characteristics of Python is the multiparadigm coding, meaning that it supports several programing paradigms in its code. Paradigms can be the object-oriented, procedural, functional, among others. There are also libraries with functional features such as itertools or functools (of which we will go into details later) that are included in the basic installation.

First baby steps

One of the main characteristics of the functional paradigm is the possibility to have methods that receive or return code. This is difficult to understand in some languages but in Python, things are easier, for example:

First approximation for basic operations in functional Python.
def calc (f, x, y):
  return f(x, y)

def subtract (x, y):
  return x - y

def mult (x, y):
  return x * y

calc(subtract, 10, 3) # 7
calc(mult, 2, 4) # 8

Walk

Perhaps the hardest thing to understand in the functional paradigm is that there are no iterative cycles, so if you have ever programmed in some object-oriented language you maybe think that without iterative cycles you can not do much or you can not generate short and clean code. You are right, partially. The functional paradigm does not have something like a for or a while loop, but it does have functions that can replace the behavior of the loops.

Below is a classic example of iterative loops in Python:

Increment in list elements example.
integer_list = [7, 8, 9]

def increment (x):
  return x + 1

def increment_in_list (list):
  result = []
  for integer in list:
    result.append(increment(integer))
  return result

increment_in_list(integer_list) # [8, 9, 10]

If we want to do that in a functional way then we can use map, a method that applies a custom function to all the elements of a list.

Increment in list elements with map.
integer_list = [7, 8, 9]

def increment (x):
  return x + 1

map(increment, integer_list) # [8, 9, 10]

Another function that allows iterative operations is filter, which creates a new list with the elements that return true when certain function is applied to a list, for example:

Filter method example.
integer_list = [7, 8, 9]

def greater_than (x, y):
  return True if x > y else False

filter(lambda x: greater_than(x, 7), integer_list) # [8, 9]

These are really useful and simple functions to start coding but sadly, also have some limitations (maybe you noticed them), for example, functions to apply to the lists can not have more than one input argument. This is not a problem. Soon we will study even more powerful functional methods.

Take a break

How do you feel until now? You’ve come a long way, congratulations! You have almost all the knowledge to enter the big leagues of the big leagues, you just have to learn something else: Lambda expressions, described as one line functions (perhaps you know them in other languages as anonymous functions). Below we rewrite the increment in list elements example using lambda expressions.

Increment in list elements with lambda expression.
integer_list = [7, 8, 9]

map(lambda x: x + 1,integer_list) # [8, 9, 10]

Run Forrest, Run!

We are now next to world-class athletes and we can learn a lot from them. One of the most prominent is itertools, a module designed to make efficient loops in iterable objects, based on languages such as Haskell and SML. In fact, you already know some functions of this module like map and filter, but now they come with the whole family and on steroids.

Some of the representative methods of this library are:

Example of some itertools methods.
import itertools

lowercases = ['a','b','c']
uppercases = ['A','B','C']
number_as_string = '1111222334'

""" Chain, allows you to concatenate iterative structures """
list(itertools.chain(uppercases, lowercases))
# ['A', 'B', 'C', 'a', 'b', 'c']

""" Permutations, returns the permutations of n elements in an iterable structures """
list(itertools.Permutations(uppercases, 2))
# [('A','B'),('A','C'),('B','A'),('B','C'),('C','A'),('C','B')]

""" Groupby, group up elements of a data structure based on a condition or rule """
[list(g) for k, g in itertools.groupby(number_as_string)]
# [['1', '1', '1', '1'], ['2', '2', '2'], ['3', '3'], ['4']]

""" Repeat, returns an element as many times as specified """
list(itertools.repeat('A',6))
# ['A', 'A', 'A', 'A', 'A', 'A']

""" Islice, returns n elements of an iterative structure """
list(itertools.islice(number_as_string,5))
# ['1', '1', '1', '1', '2']

Not all functional approaches in Python are manifested as libraries, there are also functional features that are achieved by just writing our code in a certain way. One of these ways is currying, which is defined as the transformation of a function that receives several input parameters to a sequence of functions that receives a single parameter. Why would we do this? Well, this is linked with laziness and functions that create functions, currying allows a partially execution of a function, making runtime more efficient by avoiding the calculation of every operation from the beginning. Below is an example of the purpose of currying.

Example of currying in Python.
def curried_product (a):
    def product(b):
        return a * b
    return product

curried_product(2) # function...
curried_product(2)(3)
# 6

mult = curried_product(3) # function...
mult(4)
# 12

Learning to fly

How about taking our trip to the next level? We will learn something more sophisticated and exclusive than all of the above. I will teach you functools, a module with higher-level functions, created with the specific purpose of making Python more functional. This module, like itertools, is in the core of Python.

Some examples of how to use it:

Example of some functools methods.
import functools

""" Partial, generates a function by partially executing an input function """
def multiply(a,b):
  return a * b

partial_multiply = partial(multiply,6)
print(partial_multiply(2)) # 12

""" Reduce, applies a function of 2 input arguments to a data structure """
functools.reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]) # 15

""" Update_wrapper, copy attributes from one function to another """
from functools import update_wrapper
def foo():
  """This is a foo attribute"""
 pass

def bar():
 pass

update_wrapper(bar, foo)
bar.__doc__  # 'This is a foo attribute'

You can even find fantastic external libraries that will help you to raise your code to a higher functional level. Some of these external libraries are PyMonad and Pydash.

Limit is in your mind

Here we are, the end of our trip together. But the road does not end at all. We have only taught you how to hit the road but you are the one who decides where to go. Python is a powerful language driven by thousands of people around the world who use their free time to create and improve code for all of us to use. That’s why, daily, the limitations of Python are disappearing, leaving the limits only in our mind.

Meme about functional Python
Figure 1. What some developers think about multiparadigm coding.

Functional Python is about doing things in the most optimal way possible. The first thing we must change is our way of thinking. Humans are reluctant to change, we are afraid of the new but from time to time there are some specimens who open their minds and take risks, those are who drive humanity to a superior level. Why not take the risk using functional Python then? Nothing to lose. We actually take the risk with functional Python and the result was one of our most awesome and acclaimed products: Asserts.

Conclusions

Python is, without doubt, a very useful language that collects the best of different worlds. Due to its multiparadigm nature, it is not a problem if we experiment with different paradigms in the same code and for that reason we should not limit ourselves to just one. Each paradigm has advantages and disadvantages compared to the others.

Possibly your code in Python is object oriented and that’s fine, it is a great opportunity to analyze your code and see what you can transform or create with any of the tools that you have seen here or that you can learn by yourself. I already told you, the limit is in your mind. Start refactoring small components to be functional, this will give you more confidence and change a bit the way you see the world and the way you solve problems. Nothing more grateful than a good code. A code that over the years continues efficient and useful, that does not lose validity. Our advice is to take a deep breath and get on the road to functional Python.


Author picture

Oswaldo José Parada Cuadros

Mechanical Engineer

Family, friends and little details. There is the answer.



Related




Service status - Terms of Use