DEV Community

Cover image for Introduction to functional programming with Python examples
Duomly
Duomly

Posted on • Updated on • Originally published at blog.duomly.com

Introduction to functional programming with Python examples

This article was originally published at: https://www.blog.duomly.com/the-most-important-aspects-of-functional-programming-with-python-examples/

Functional programming is an interesting programming concept which gains a lot of attention lately. This article presents some of the most important aspects of functional programming in general and provides several examples in Python.

Functional programming is a kind of the declarative programming paradigm where functions represent relations among objects, like in mathematics. Thus, functions are much more than ordinary routines.

This programming paradigm can be implemented in a variety of languages. There are several functional programming languages such as Closure, Erlang or Haskel. Many languages support functional programming in addition to other paradigms: C++, C#, F#, Java, Python, JavaScript and others.

In this article, you’ll find explanations on several important principles and concepts related to functional programming:

  • pure functions,
  • anonymous functions,
  • recursive functions,
  • first-class functions,
  • immutable data types.

Pure Functions

A pure function is a function that:

  • is idempotent — returns the same result if provided the same arguments,
  • has no side effects.

If a function uses an object from a higher scope or random numbers, communicates with files and so on, it might be impure because its result doesn’t depend only on its arguments.

A function that modifies objects outside of its scope, write to files, prints to the console and so on, have side effects and might be impure as well.

Pure functions usually don’t use objects from outer scopes and thus avoid shared states. This might simplify a program and help escape some errors.

Anonymous Functions

Anonymous (lambda) functions can be very convenient for functional programming constructs. They don’t have names and usually are created ad-hoc, with a single purpose.

In Python, you create an anonymous function with the lambda keyword:

lambda x, y: x + y

The above statement creates the function that accepts two arguments and returns their sum. In the next example, the functions f and g do the same thing:

>>> f = lambda x, y: x + y
>>> def g(x, y):
return x + y

Recursive Functions

A recursive function is a function that calls itself during the execution. For example, we can use recursion to find the factorial in the functional style:

>>> def factorial_r(n):
if n == 0:
return 1
return n * factorial_r(n - 1)

Alternatively, we can solve the same problem with the while or for loop:

>>> def factorial_l(n):
if n == 0:
return 1
product = 1
for i in range(1, n+1):
product *= i
return product

First Class Functions

In functional programming, functions are the first-class objects, also called higher-order functions — the data types treated the same way as other types.

Functions (or, to be more precise, their pointers or references) can be passed as arguments to and returned from other functions. They can also be used as variables inside programs.

The code below illustrates passing the built-in function max as an argument of the function f and calling it from inside f.

>>> def f(function, *arguments):
return function(*arguments)

>>> f(max, 1, 2, 4, 8, 16)
16

Very important functional programming concepts are:

  • mapping,
  • filtering,
  • reducing.

They are all supported in Python.

Mapping is performed with the built-in class map. It takes a function (or method, or any callable) as the first argument and an iterable (like a list or tuple) as the second argument, and returns the iterator with the results of calling the function on the items of the iterable:

>>> list(map(abs, [-2, -1, 0, 1, 2]))
[2, 1, 0, 1, 2]

In this example, the built-in function abs is called with the arguments -2, -1, 0, 1 and 2, respectively. We can obtain the same result with the list comprehension:

>>> [abs(item) for item in [-2, -1, 0, 1, 2]]
[2, 1, 0, 1, 2]

We don’t have to use built-in functions. It is possible to provide a custom function (or method, or any callable). Lambda functions can be particularly convenient in such cases:

>>> list(map(lambda item: 2 * item, [-2, -1, 0, 1, 2]))
[-4, -2, 0, 2, 4]

The statement above multiplied each item of the list [-2, -1, 0, 1, 2] with 2 using the custom (lambda) function lambda item: 2 * item. Of course, we can use the comprehension to achieve the same thing:

>>> [2 * item for item in [-2, -1, 0, 1, 2]]
[-4, -2, 0, 2, 4]

Filtering is performed with the built-in class filter. It also takes a function (or method, or any callable) as the first argument and iterable as the second. It calls the function on the items of the iterable and returns a new iterable with the items for which the function returned True or anything that is evaluated as True. For example:

>>> list(filter(lambda item: item >= 0, [-2, -1, 0, 1, 2]))
[0, 1, 2]

The statement above returns the list of non-negative items of [-2, -1, 0, 1, 2], as defined with the function lambda item: item >= 0. Again, the same result can be achieved using the comprehension:

>>> [item for item in [-2, -1, 0, 1, 2] if item >= 0]
[0, 1, 2]

Reducing is performed with the function reduce from the module functools. Again, it takes two arguments: a function and iterable. It calls the function on the first two items of the iterable, then on the result of this operation and the third item and so on. It returns a single value. For example, we can find the sum of all items of a list like this:

>>> import functools
>>>
>>> functools.reduce(lambda x, y: x + y, [1, 2, 4, 8, 16])
31

This example is just for illustration. The preferred way of calculating the sum is using the built-in reducing function sum:

>>> sum([1, 2, 4, 8, 16])
31

Immutable Data Types

An immutable object is an object whose state can’t be modified once it’s created. Contrary, a mutable object allows changes in its state.

Immutable objects are generally desirable in functional programming.

For example, in Python, lists are mutable, while tuples are immutable:

>>> a = [1, 2, 4, 8, 16] >>> a[0] = 32 # OK. You can modify lists.
>>> a
[32, 2, 4, 8, 16]
>>> a = (1, 2, 4, 8, 16)
>>> a[0] = 32 # Wrong! You can't modify tuples.
Traceback (most recent call last):
File "", line 1, in
TypeError: 'tuple' object does not support item assignment

We can modify lists by appending them new elements, but when we try to do this with tuples, they are not changed but new instances are created:

>>> # Lists (mutable)
>>> a = [1, 2, 4] # a is a list
>>> b = a # a and b refer to the same list object
>>> id(a) == id(b)
True
>>> a += [8, 16] # a is modified and so is b - they refer to the same object
>>> a
[1, 2, 4, 8, 16]
>>> b
[1, 2, 4, 8, 16]
>>> id(a) == id(b)
True
>>>
>>> # Tuples (immutable)
>>> a = (1, 2, 4) # a is a tuple
>>> b = a # a and b refer to the same tuple object
>>> id(a) == id(b)
True
>>> a += (8, 16) # new tuple is created and assigned to a; b is unchanged
>>> a # a refers to the new object
(1, 2, 4, 8, 16)
>>> b # b refers to the old object
(1, 2, 4)
>>> id(a) == id(b)
False

Advantages of Functional Programming

The underlying concepts and principles — especially higher-order functions, immutable data and the lack of side effects — imply important advantages of functional programs:

  • they might be easier to comprehend, implement, test and debug,
  • they might be shorter and more concise (compare two programs for calculating the factorial above),
  • they might be less error-prone,
  • they are easier to work with when implementing parallel execution.

Functional programming is a valuable paradigm worth learning. In addition to the advantages listed above, it’ll probably give you a new perspective on solving programming problems.

Duomly - programming online courses

Top comments (9)

Collapse
 
orelkan profile image
Orel Kanditan • Edited

Thanks for the article.

Personally I really don't like the syntax for functional programming in Python, it feels like it's encouraging me not to use it.. With the verbose "lambda" keyword and needing to wrap each function instead of object chaining (list.map().filter()...). I much prefer the syntax of Scala and also Javascript

Collapse
 
radekfabisiak profile image
Radoslaw Fabisiak

Had the same, my core language is Javascript, but when I started working more with Python I'm going into closer friends with his syntax.

Collapse
 
saint4eva profile image
saint4eva

Cool article. Thank you.

I also love writing functional style in C# where it makes sense, such as Linq, Lambda Expression, Recursive pattern matching, Switch expression, local function, Ranges & slices, Property pattern matching.

Once more, nice article.

Collapse
 
daveparr profile image
Dave Parr • Edited

Really good article. I come from an R background, where map, apply, reduce is very common in a number of forms.

Something I want to pick out to ensure I've understood the python variant:

In functional programming, functions are the first-class objects, also called higher-order functions — the data types treated the same way as other types.

I think this sentence might be able to be refined, or my understanding might be wrong.

First class functions are a feature of the language where functions can be passed as arguments/data structures.

Higher order functions are a subset of functions (like map as you identify later), but not all first class functions are higher order functions, as a higher order function modifies the behaviour of a function that is it's argument.

So while 'print' is a first class finction, e.g. it can be given as an argument, 'map' is a higher order function, as you can map a print over a list. Conversely you can't 'print' a 'map' over a list.

Am I understanding this correctly or is this not true in python?

Collapse
 
zeljkobekcic profile image
TheRealZeljko

Would be nice to have something like the Maybe-Monad in the functools package for example. Sometimes python's None is a valid output and can not be used equivalently to Nothing.

What I like to do is to break down my problem into only functions and then build up objects from them. This way I can reduce the tests around the object and easier test the functions.

Constructive feedback:

  • I think you could have added, that map and filter return you a genrator. You wrap them in your examples in list( ... ) and get the same result as in the list comprehensions.

  • A cool thing to use is the operator module. Then you can do something like this:

from operator import add, mul
from functools import reduce
print(reduce(add, [1,2,3,4]))
# => 10
print(reduce(mul, [1,2,3,4]))
# => 24

This reduces the amount of simple lambda functions like lambda x, y: x + y and makes the code more readable.

  • Cleanly written, I really like that 👍
Collapse
 
ashraful profile image
Mohamad Ashraful Islam • Edited

The most important thing of Python is code indentation. Some of your functions looks indentation error to me. Otherwise it’s a nice article.

Collapse
 
haakobo profile image
Haakobo

Push enter after each line of code.

Collapse
 
greatbahram profile image
Bahram Aghaei

Great article! I really like it! I think, you should write another article in this matter something for intermediate users and expand this concept.

Thanks!

Collapse
 
s312569 profile image
Jason Mulvenna

You misspelt Clojure and Haskell...