[Avg. reading time: 20 minutes]
Functional Programming Concepts
Functional programming in Python emphasizes the use of functions as first-class citizens, immutability, and declarative code that avoids changing state and mutable data.
def counter():
count = 0 # Initialize the state
count += 1
return count
print(counter())
print(counter())
print(counter())
Regular functions internal State & mutable data
def counter():
# Define an internal state using an attribute
if not hasattr(counter, "count"):
counter.count = 0 # Initialize the state
# Modify the internal state
counter.count += 1
return counter.count
print(counter())
print(counter())
print(counter())
Internal state & immutability
Example without Lambda
increment = lambda x: x + 1
print(increment(5)) # Output: 6
print(increment(5)) # Output: 6
Using Lambda
Lambda functions as a way to write quick, one-off functions without defining a full function using def.
Example without Lambda
def square(x):
return x ** 2
print(square(4))
Using Lambda
square = lambda x: x ** 2
print(square(4))
Without Lambda
def get_age(person):
return person['age']
people = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]
# Using a defined function to sort
sorted_people = sorted(people, key=get_age)
print(sorted_people)
Using Lambda
people = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]
# Using a lambda function to sort
sorted_people = sorted(people, key=lambda person: person['age'])
print(sorted_people)
Map, Filter, Reduce Functions
Map, filter, and reduce are higher-order functions in Python that enable a functional programming style, allowing you to work with data collections in a more expressive and declarative manner.
Map
The map() function applies a given function to each item of an iterable (like a list or tuple) and returns an iterator with the results.
Map Without Functional Approach
numbers = [1, 2, 3, 4, 5]
squares = []
for num in numbers:
squares.append(num ** 2)
print(squares) # Output: [1, 4, 9, 16, 25]
Map With Lambda and Map
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # Output: [1, 4, 9, 16, 25]
Filter
The filter() function filters items out of an iterable based on whether they meet a condition defined by a function, returning an iterator with only those elements for which the function returns True.
Filter Without Functional Approach
numbers = [1, 2, 3, 4, 5]
evens = []
for num in numbers:
if num % 2 == 0:
evens.append(num)
print(evens) # Output: [2, 4]
Filter using Functional Approach
numbers = [1, 2, 3, 4, 5]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # Output: [2, 4]
Reduce
The reduce() function, from the functools module, applies a rolling computation to pairs of values in an iterable. It reduces the iterable to a single accumulated value.
At the same time, in many cases, simpler functions like sum() or loops may be more readable.
Reduce Without Functional Approach
- First, 1 * 2 = 2
- Then, 2 * 3 = 6
- Then, 6 * 4 = 24
- Then, 24 * 5 = 120
numbers = [1, 2, 3, 4, 5]
product = 1
for num in numbers:
product *= num
print(product) # Output: 120
Reduce With Lambda
from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # Output: 120
Using an Initliazer
from functools import reduce
numbers = [1, 2, 3]
# Start with an initial value of 10
result = reduce(lambda x, y: x + y, numbers, 10)
print(result)
# Output: 16
Using SUM() instead of Reduce()
# So its not necessary to use Reduce all the time :)
numbers = [1, 2, 3, 4, 5]
# Using sum to sum the list
result = sum(numbers)
print(result) # Output: 15
String Concatenation
from functools import reduce
words = ['Hello', 'World', 'from', 'Python']
result = reduce(lambda x, y: x + ' ' + y, words)
print(result)
# Output: "Hello World from Python"
List Comprehension and Generators
List Comprehension
List comprehension offers a shorter syntax when you want to create a new list based on the values of an existing list.
Generates the entire list in memory at once, which can consume a lot of memory for large datasets.
Uses: [ ]
Without List Comprehension
numbers = [1, 2, 3, 4, 5]
squares = []
for num in numbers:
squares.append(num ** 2)
print(squares)
With List Comprehensions
numbers = [1, 2, 3, 4, 5]
squares = [x ** 2 for x in numbers]
print(squares)
List Generator
Generator expressions are used to create generators, which are iterators that generate values on the fly and yield one item at a time.
Generator expressions generate items lazily, meaning they yield one item at a time and only when needed. This makes them much more memory efficient for large datasets.
Uses: ( )
numbers = [1, 2, 3, 4, 5]
squares = (x ** 2 for x in numbers)
print(sum(squares)) # Output: 55
numbers = [1, 2, 3, 4, 5]
squares = (x ** 2 for x in numbers)
print(list(squares))
Best suited
- Only one line is in memory at a time.
- Suitable for processing large or infinite data streams.