Functional programming (FP) is a paradigm that treats computation as the evaluation of pure functions and avoids mutable state. Python supports FP concepts like higher-order functions, function composition, currying, monads, and lazy evaluation.

This article explores advanced FP techniques in Python, with real-world examples to improve your coding skills.


1️⃣ Higher-Order Functions

A higher-order function is a function that either:
Takes another function as an argument
Returns a function

Example: Using `map`, `filter`, and `reduce`

from functools import reduce

# Square each number
numbers = [1, 2, 3, 4, 5]  
squared = list(map(lambda x: x**2, numbers))  
print(squared)  # Output: [1, 4, 9, 16, 25]

# Filter even numbers
evens = list(filter(lambda x: x % 2 == 0, numbers))  
print(evens)  # Output: [2, 4]

# Reduce: Sum of numbers
sum_numbers = reduce(lambda x, y: x + y, numbers)  
print(sum_numbers)  # Output: 15  

2️⃣ Function Composition

Function composition combines multiple functions into a single function.

Example: Chaining Functions

def double(x):  
return x * 2

def square(x):  
return x ** 2

# Compose functions: (double ∘ square)(x)
def compose(f, g):  
return lambda x: f(g(x))

double_then_square = compose(square, double)  
print(double_then_square(3))  # Output: (3 * 2)^2 = 36  

🔹 Why Use It?
✔ Encourages modularity
✔ Reduces side effects


3️⃣ Closures & Partial Functions

Closures allow functions to retain state even after execution.

Example: Closure for Custom Logging

def logger(level):  
def log(message):  
print(f"[{level.upper()}] {message}")  
return log

info_log = logger("info")  
info_log("Server started")  # Output: [INFO] Server started  

Example: Using `functools.partial`

from functools import partial

def power(base, exponent):  
return base ** exponent

square = partial(power, exponent=2)  
print(square(5))  # Output: 25  

4️⃣ Lazy Evaluation & Generators

Lazy evaluation defers computation until needed.

Example: Infinite Fibonacci Generator

def fibonacci():  
a, b = 0, 1  
while True:  
yield a  
a, b = b, a + b

fib = fibonacci()  
print(next(fib))  # 0  
print(next(fib))  # 1  
print(next(fib))  # 1  

5️⃣ Monads: Handling Side Effects

Monads wrap values and apply transformations while preserving state.

Example: `Maybe` Monad (Avoiding `None` Errors)

class Maybe:  
def __init__(self, value):  
self.value = value

    def bind(self, func):  
        return Maybe(func(self.value)) if self.value is not None else Maybe(None)  

    def __repr__(self):  
        return f"Maybe({self.value})"  

def safe_divide(x):  
return lambda y: x / y if y != 0 else None

result = Maybe(10).bind(safe_divide(2))  # Maybe(5.0)  
print(result)

result = Maybe(10).bind(safe_divide(0))  # Maybe(None)  
print(result)  

✔ Prevents `NoneType` errors without excessive `if` statements!


Conclusion

Python’s functional programming techniques make code expressive, modular, and bug-resistant. Key takeaways:

Use higher-order functions (`map`, `filter`, `reduce`)
Compose functions for modular code
Leverage closures and partial functions
Use lazy evaluation for performance efficiency
Apply monads to manage side effects

📌 Master these techniques to write cleaner, scalable, and efficient Python code! 🚀