Functional Programming in Python
What Is Functional Programming?
Functional Programming in Python is a coding style that treats computations as evaluations of mathematical functions, minimizing changes to state and mutable data. In Python, this paradigm can be utilized to write cleaner, more efficient code. Through functions like map, filter, reduce, and comprehensions, Python supports functional programming concepts, allowing developers to write code that's easier to understand, test, and debug.
This method can result in outcomes that are more foreseeable and bug-free code by focusing on what to solve rather than how to solve.
Functional Programming Concepts
Functional Programming in Python is a paradigm that leans heavily on a few core principles to ensure code is concise, easy to understand, and bug-free. Here's a look at these essential concepts:
-
Pure Functions: Think of pure functions as the dependable workhorses of functional programming in python. These functions guarantee the same result every time you feed them the same input, no surprises. They're like a perfectly calibrated machine that, no matter the external circumstances, performs its task flawlessly without meddling with the outside world or altering its inputs.
-
Recursion: Imagine climbing a staircase, but instead of taking one step at a time, each step involves a smaller staircase to conquer. This is recursion in a nutshell. Functional programming in python skips traditional loops and instead uses recursion for repetitive tasks, breaking down problems into simpler, more manageable pieces until a base condition is met.
-
First-Class and Higher-Order Functions: In functional programming, functions are VIPs. They can go anywhere, do anything: get passed around as arguments, returned from other functions, or stored for later use, just like any other variable. Higher-order functions take this a step further by operating on other functions, either by taking them as arguments or returning them, enabling a flexible and powerful way to compose software.
-
Immutable Variables: Imagine writing in pen on a magical paper that never lets you erase or overwrite your words; you can only get a new sheet to start anew. That's how variables work here. Once a variable is set, it's set for good. If you need to change it, you create a new variable. This immutability makes code more predictable and safer from accidental changes.
By embracing these principles, functional programming in python aims to streamline code into a series of simple, predictable transformations, making it easier to write, test, and maintain.
Functional Programming in Python
Pure Functions
As we know, we have 2 characteristics that a function should have for it to be a pure function.
- The output of a pure function remains the same for the same inputs, for example, a function that subtracts 2 numbers will always give the same output -- sub(7, 3) will always give the output as 4, no matter what.
- There are no side effects of a pure function.
For better understanding, let's elaborate the term side-effects. The term side-effects is a common term you must have heard when buying medication. It refers to some consequences of the medicine that are unintended.
The same analogy can be applied to programing. A python function has a side-effect if it modifies it's calling environment - for example changing the values of the argument passed to the function.
Let's take a look at an example of a pure function.
Output:
In the function above, we have both the characteristics of a pure function. The function will return the same values for the same input. The input [1, 2, 3, 4] will always return [10, 20, 30, 40]. At the same time, since this function does not change any variables in the environment and does not have any side-effects, it is definitely a pure function.
Let's look at another function.
Output:
Is this function a pure function? No, since the function modifies the value of global variables - a_list.
Recursion
Python utilizes recursion, an approach where resolving a problem relies on addressing smaller versions of the same problem, as an alternative to traditional looping mechanisms like for and while loops. A classic example of recursion in Python is a function that calculates the factorial of a number:
Output:
First-Class Functions and Higher-Order Functions
Applying a Function to an Iterable With map()
This function gives us the ability to apply any function to every element on an object that is iterable (an object over which we can iterate, or use a loop). For example, if we wanted to append a greeting to multiple names in a list, we could do the following:
Output:
Selecting Elements From an Iterable With filter()
This function, as the name suggests, filters out some values from an iterable according to a specified condition. The filter function returns either True or False.
Let's say we want to filter out the numbers that are even from a list of numbers, we can do it the following way.
Output:
You can even combine the two functions - map and filter and use them for expensive data manipulations!
Defining an Anonymous Function With lambda
Lambda expressions are another concept of functional programming. They are essentially anonymous functions. While creating functions in python, we usually use the def keyword and give the function a unique name. However, lambda expressions allow us to skip that process and write small functions much quicker.
The syntax of lambda expression is as follows:
Read more about the lambda function
For example, we can write a higher order function that returns the nthsup> power of a number using a lambda expression.
Output:
The higher order function returned to us the 3rd power of 4 which is 64.
Immutability
Moving on to the second characteristic - immutability. As stated earlier, immutability means inability to change.
Have you ever faced a bug while coding where you set the value of a variable to say, 100, and it became None? Say that the variable was immutable, then an error would have been thrown at the stage in the code where the value of that variable was being modified and not where the modified value already affected your software -- we can find the root cause of the bug much earlier.
Python has a certain set of immutable data types, with one of the commonly used one being the Tuple. Let's compare the tuple to a mutable data type - list.
Output:
Upon trying to change the value, we recieve a TypeError here, however, take a look at the interesting scenario below, where we make a change to the immutable tuple.
Output:
You must be wondering how this worked? This is because the element at the second index in this tuple is a list, and lists are mutable. If you try to change it back to [2, 3, 4], you will encounter TypeError once again since we can change the elements present in a mutable data type inside of the tuple, but we cannot modify the reference to the mutable object that is stored in memory.
Functional vs. object-oriented programming
Feature | Functional Programming | Object-Oriented Programming |
---|---|---|
Core Concept | Uses immutable data and functions as first-class citizens. Emphasizes the application of functions. | Organizes software design around data, or objects, with state and behavior. |
Data Handling | Data is immutable. Functions operate on data without changing it. | Data is often encapsulated within objects, which can be manipulated through methods. |
State and Side Effects | Avoids side effects and maintains state in a controlled manner. | Manages state and side effects through object instances and their methods. |
Modularity | Achieved through functions that can be composed and reused. | Achieved through objects and classes that encapsulate data and functionality. |
Concurrency | Naturally suited for concurrent execution due to immutability and stateless functions. | Requires careful management of objects and their states to safely execute in a concurrent manner. |
Code Reuse | Through higher-order functions and function composition. | Through inheritance and polymorphism, allowing objects to take on multiple forms. |
Design Focus | Focuses on what the program should accomplish via expressions and declarations. | Focuses on how to structure the program, organizing it into classes and objects. |
Typical Use Cases | Well-suited for applications where predictability and state management are critical, such as in UIs or financial software. | Commonly used in applications that require extensive data manipulation and have complex state management, like software applications, games, and systems software. |
Examples in Python | Functions like map, filter, reduce, and comprehensions. Use of lambda for anonymous functions. | Class definitions, objects, inheritance, encapsulation, and polymorphism. |
Flexibility | Functional programming is more about solving problems in a declarative way, focusing on the "what". | Object-oriented programming provides a structured way to organize code, focusing on the "how". |
Conclusion
- Functional Programming in Python promotes writing code that is easier to understand, test, and maintain by focusing on pure functions without side effects.
- By emphasizing immutability, functional programming reduces the chances of bugs related to changing states, making programs more predictable.
- Concepts like first-class functions, higher-order functions, and the use of functions like map, filter, and lambda enable concise and expressive code.
- The statelessness and immutability in functional programming are advantageous for parallel and concurrent execution, leading to potentially better performance.
- Despite being an object-oriented language at heart, Python adeptly supports functional programming techniques, offering developers the best of both worlds for a wide range of programming challenges.