Advanced Python
Chapter 03: Functional Programming in Python
• Introduction to functional programming
• Functional programming Principals
• Lambda functions
• Map, filter, and reduce functions
• Generators and iterators (Yields and Next function)
• Decorators
Advanced Python
Functional Programming in Python
Introduction to functional programming
Python supports multiple programming paradigms:
• Imperative programming
• Functional programming
• Object-oriented programming (OOP)
• Procedural programming
• Declarative programming
Advanced Python
Functional Programming in Python
● Functional programming (FP) is a programming paradigm that focuses on the
use of functions to solve programming problems.
● Functional Programming treats computation as the evaluation of mathematical
functions and avoids changing state or mutable data.
Advanced Python
Functional Programming Principles
● Core Principles of Functional Programming Functional programming is built
upon several fundamental principles that guide its approach to software
development:
● Pure Functions
● Immutability
● First-Class Functions
● Higher-Order Functions (HOFs)
● Recursion
● Lazy Evaluation
● Function Composition
Advanced Python
Functional Programming Principles
● Pure functions: In functional programming, pure functions are functions that
have no side effects and always return the same output given the same input.
Pure functions are easier to reason about, test, and parallelize.
● Characteristics of Pure Functions:
● Deterministic: Always returns the same output for the same input.
● No Side Effects: Does not modify global variables, perform I/O operations,
or change the state of objects passed as arguments.
● Testable: Easy to unit test as their behavior is isolated and predictable.
Advanced Python
Functional Programming Principles
Pure functions:
Example 01:
def square(x):
return x ** 2
sequare(3)
Advanced Python
Functional Programming Principles
Pure functions:
Example 02:
def add(a, b):
return a + b
result = add(5, 3) # Returns 8
Advanced Python
Functional Programming Principles
Pure functions
Example 03:
def multiply(a, b):
return a * b
result = multiply(4, 7) # Returns 28
Advanced Python
Functional Programming Principles
Pure functions
Example 04:
def concatenate_strings(s1, s2):
return s1 + s2
result = concatenate_strings("Hello, ", "world!") # Returns "Hello, world!"
Advanced Python
Functional Programming Principles
● Impure Functions: An impure function, conversely, may produce different
outputs for the same input or cause side effects. These functions often interact
with external state, such as global variables, databases, or user input.
● While functional programming encourages minimizing impure functions, they
are often necessary for real-world applications involving I/O, database
interactions, or user interfaces.
Advanced Python
Functional Programming Principles
● Impure Function Example:
counter = 0
def increment(x):
global counter
counter += x
return counter
print(increment(5)) # Output: 5
print(increment(5)) # Output: 10 (depends on previous state!)
Advanced Python
Functional Programming Principles
● Immutability: Data structures are not modified after creation. Instead, any
operation that would typically alter a data structure in an imperative paradigm
results in the creation of a new data structure with the desired changes. This
prevents unexpected side effects and simplifies concurrent programming.
Advanced Python
Functional Principles
● First-Class Functions: Functions are treated as first-class citizens, meaning
they can be assigned to variables, passed as arguments to other functions, and
returned as values from other functions. This flexibility is crucial for building
higher-order functions and enabling dynamic behavior.
Advanced Python
Functional Programming Principles
● Higher-order functions(HOFs): Python supports higher-order functions, which
are functions that take one or more functions as arguments or return a function
as their result.
● HOFs are powerful abstractions that can be used to create more generic , flexible
and reusable code.
Advanced Python
Functional Programming Principles
Higher-order functions Example:
def apply_operation(func, x, y):
return func(x, y)
def add(x, y):
return x + y
def multiply(x, y):
return x * y
result = apply_operation(add, 2, 3) # result is 5
result = apply_operation(multiply, 2, 3) # result is 6
Advanced Python
Functional Programming Principles
● Recursion: In functional programming, recursion is often preferred over
explicit loops for iterative processes. It involves a function calling itself
until a base condition is met.
Advanced Python
Functional Programming Principles
● Lazy Evaluation: Values are computed only when they are actually
needed, rather than immediately. This can lead to performance
optimizations and the ability to work with potentially infinite data streams
(e.g., using generators and iterators in Python).
Advanced Python
Functional Programming Principles
● Function Composition: This involves combining simpler functions to build
more complex ones. By chaining functions together, data flows through a
series of transformations, leading to clear and modular code.
Advanced Python
Functional Programming : lambda function
Lambda function
● A lambda function, also known as an anonymous function, is a small, single-expression
function that doesn't have a name. Lambda functions can be defined inline within
expressions or passed as arguments to other functions.
● They are particularly useful for short, throwaway functions that are used as arguments to
higher-order functions.
● A lambda function can take any number of arguments, but can only have one expression.
Syntax:
lambda arguments: any expression
Advanced Python
Functional Programming : lambda function
● Key Characteristics:
● Anonymous: They do not have a name.
● Single Expression: They can only contain one expression, not multiple
statements, assignments, or loops.
● Concise: Ideal for simple operations where a full function definition would be verbose.
Syntax:
lambda arguments: any expression
Advanced Python
Functional Programming : lambda()
Example 01:
square = lambda x: x ** 2
result = square(5) # result is 25
Example 02:
add = lambda x, y: x + y
result = add(2, 3)
print(result) # prints 5
Advanced Python
Functional Programming : lambda()
Example 03:
multiply = lambda x, y, z: x * y * z
result = multiply(2, 3, 4)
print(result) # prints 24
Advanced Python
Functional Programming : lambda()
Example 04: Example 05:
Max = lambda a, b : a if(a > b) else b str1 = 'welcome'
print(Max(1, 2)) upper = lambda string: [Link]()
print(upper(str1))
Advanced Python
Functional Programming : lambda()
Example 06:
def myfunc(n):
return lambda a : a * n
mydoubler = myfunc(2)
mytripler = myfunc(3)
print(mydoubler(13))
print(mytripler(13))
Advanced Python
Functional Programming: lambda()
Example 07: (with list comprehension)
l= [lambda arg=x: arg * 10 for x in range(1, 5)]
for item in l:
print(item()) # [10,20,30,40]
Note: A Python list comprehension consists of brackets containing the expression, which is executed
for each element along with the for loop to iterate over each element in the Python list.
Advanced Python
Functional Programming
Example 08:
List = [[2,3,4],[1, 4, 16, 64],[3, 6, 9, 12]]
sortList = lambda x: (sorted(i) for i in x)
secondLargest = lambda x, f : [y[len(y)-2] for y in f(x)]
res = secondLargest(List, sortList)
print(res) #output: [3, 16, 9]
● It uses lambda functions to sort each sublist and find the second-largest element in
each sublist. The result is a list of second-largest elements, which is then printed.
The output displays the second-largest element from each sublist in the original list.
Advanced Python
Functional Programming
True or False:
1. Lambda functions can have multiple expressions.
2. Lambda functions have only one argument.
Advanced Python
Functional Programming : map()
map():
● In Python, map() is a built-in function that applies a given function to each
element of an iterable and returns a new iterable with the transformed values.
The map() function is useful for performing operations on all elements of a list,
tuple, or other iterable without having to write a loop.
● function returns a map object(which is an iterator) of the results after applying
the given function to each item of a given iterable (list, tuple etc.)
Advanced Python
Functional Programming : map()
Syntax:
map(function, iterable)
Where function is the function to apply to each element of the iterable, and
iterable is the object that you want to apply the function to.
Advanced Python
Functional Programming : map()
Example 01:
my_list = [1, 2, 3, 4, 5]
squared_list = list(map(lambda x: x ** 2, my_list))
print(squared_list) # prints [1, 4, 9, 16, 25]
Advanced Python
Functional Programming : map()
Example 2: Converting strings in a list to uppercase using map()
my_list = [‘mango', 'banana', 'cherry‘, ’kiwi’, ‘grape’]
uppercase_list = list(map(lambda x: [Link](), my_list))
print(uppercase_list) # prints ['MANGO', 'BANANA', 'CHERRY‘, ’KIWI’,
‘GRAPE’]
Advanced Python
Functional Programming : map()
Example 3: Finding the length of each string in a list using map()
my_list = ['apple', 'banana', 'cherry‘, ‘kiwi’, ‘grape’]
length_list = list(map(lambda x: len(x), my_list))
print(length_list) # prints [5, 6, 6, 4, 5]
Advanced Python
Functional Programming : map()
Example 4: Using map() with multiple iterables
my_list1 = [1, 2, 3]
my_list2 = [10, 20, 30]
product_list = list(map(lambda x, y: x * y, my_list1, my_list2))
print(product_list) # prints [10, 40, 90]
Advanced Python
Functional Programming: map()
Example 5: Using map() with Strings
# List of strings
l = ['sat', 'bat', 'cat', 'mat']
# map() can listify the list of strings individually
test = list(map(list, l))
print(test)
#[['s', 'a', 't'], ['b', 'a', 't'], ['c', 'a', 't'], ['m', 'a', 't']]
Advanced Python
Functional Programming: filter()
filter():
● In Python, filter() is a built-in function that applies a given function to each element of
an iterable and returns a new iterable with only the elements that satisfy the condition.
Returns: an iterator that is already filtered.
Syntax:
filter(function, iterable)
Where function is the function to apply to each element of the iterable, and iterable is the object
that you want to apply the function to.
Advanced Python
Functional Programming : filter()
Example 1: Filtering out even numbers from a list using filter()
my_list = [1, 2, 3, 4, 5]
even_list = list(filter(lambda x: x % 2 == 0, my_list))
print(even_list) # prints [2, 4]
Advanced Python
Functional Programming : filter()
Example 2: Filtering out strings that contain a specific substring using filter()
my_list = ['apple', 'banana', 'cherry']
filtered_list = list(filter(lambda x: 'a' in x, my_list))
print(filtered_list) # prints ['apple', 'banana']
Example 3: Filtering out non negative numbers from a list using filter()
my_list = [-2, 0, 3, -5, 6]
positive_list = list(filter(lambda x: x >= 0, my_list))
print(positive_list) # prints [0, 3, 6]
Advanced Python
Functional Programming: filter()
Example 4: Filtering out not empty strings from a list using filter()
my_list = ['apple', 'banana', 'cherry‘, ‘kiwi’, ‘grape’]
nonempty_list = list(filter(lambda x: x != '', my_list))
print(nonempty_list) # prints ['apple', 'banana', 'cherry‘, ‘kiwi’, ‘grape’]
Example 5: Using filter() with multiple conditions
my_list = [1, 2, 3, 4, 5]
filtered_list = list(filter(lambda x: x > 2 and x < 5, my_list))
print(filtered_list) # prints [3, 4]
Advanced Python
Functional Programming: filter()
Example 6: filter out numbers that are multiple of 3
def is_multiple_of_3(num):
return num % 3 == 0
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = list(filter(lambda x: is_multiple_of_3(x), numbers))
print(result)
#[3, 6, 9]
Advanced Python
Functional Programming: reduce()
reduce() function
● In Python, reduce() is a built-in function that applies a given function to the elements of
an iterable and returns a single value. The reduce() function is useful for performing
operations that combine multiple elements of a list, tuple, or other iterable.
Syntax:
reduce(function, iterable[, initializer])
Where function is the function to apply to each element of the iterable, and iterable is the object that you want to
apply the function to. The initializer parameter is optional and is used to specify an initial value for the
reduction.
Advanced Python
Functional Programming : reduce()
Example 1: Finding the sum of a list using Working
reduce()
Iteration 1: x = 1 (first element), y = 2, result = 3
from functools import reduce
Iteration 2: x = 3 (previous result), y = 3, result = 6
my_list = [1, 2, 3, 4, 5]
Iteration 3: x = 6 (previous result), y = 4, result = 10
sum = reduce(lambda x, y: x + y,
my_list) Iteration 4: x = 10 (previous result), y = 5, result =
15
print(sum) # prints 15
Advanced Python
Functional Programming : reduce()
Example 2: Finding the product of a list using reduce()
from functools import reduce
my_list = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, my_list)
print(product) # prints 120
Example 3: Finding the maximum element of a list using reduce()
from functools import reduce
my_list = [1, 5, 3, 4, 2]
maximum = reduce(lambda x, y: x if x > y else y, my_list)
print(maximum) # prints 5
Advanced Python
Functional Programming: reduce()
Example 4: Combining strings in a list using reduce()
from functools import reduce
my_list = [‘mango', 'banana', 'cherry']
combined_string = reduce(lambda x, y: x + ', ' + y, my_list)
print(combined_string) # prints ‘mango, banana, cherry'
Example 5: Using reduce() with an initial value
from functools import reduce
my_list = [1, 2, 3, 4, 5]
sum = reduce(lambda x, y: x + y, my_list, 10)
print(sum) # prints 25
Advanced Python
Functional Programming
Generator
● A generator function is a special type of function that returns an iterator, which can be
iterated over using the yield keyword.
● A generator function in Python is defined like a normal function, but whenever it needs
to generate a value, it does so with the yield keyword rather than return. If the body of a
def contains yield, the function automatically becomes a Python generator function.
● Each time the yield statement is encountered, the function's state is saved, and the
yielded value is returned.
● It can have one or more yield.
● It return a generator object
Advanced Python
Functional Programming
Generator
● The next() function is used to retrieve the next value from the iterator.
● Python Generator functions return a generator object that is iterable, i.e., can
be used as an Iterator. Generator objects are used either by calling the next
method of the generator object or using the generator object in a “for in” loop.
● Standard Functions: Compute a value and return it once. They use the return
statement.
● Generators: Use the yield statement to return values one at a time, allowing
iteration over a sequence of values without storing the entire sequence in
memory.
Advanced Python
Functional Programming
Example 00:
# Using the generator
def test (x, y, z):
gen = test(5,6,7)
yield x
print(next(gen)) #
yield y Output: 5
yield z print(next(gen)) #
Output: 6
print(next(gen)) #
Output: 7
Advanced Python
Functional Programming
Example 01: # Using the generator
def number_generator(n): gen = number_generator(5)
print(next(gen)) # Output: 0
for i in range(n):
print(next(gen)) # Output: 1
yield i
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
print(next(gen)) # Output: 4
Advanced Python
Functional Programming
Example 03: # Using the generator
def square_generator(n): gen = square_generator(5)
for i in range(n): print(next(gen)) # Output: 0
yield i ** 2 print(next(gen)) # Output: 1
print(next(gen)) # Output: 4
print(next(gen)) # Output: 9
print(next(gen)) # Output: 16
Advanced Python
Functional Programming
Example 04: # Using the generator
def even_numbers(n): gen = even_numbers(10)
for i in range(n): print(next(gen)) # Output: 0
if i % 2 == 0: print(next(gen)) # Output: 2
yield i print(next(gen)) # Output: 4
print(next(gen)) # Output: 6
print(next(gen)) # Output: 8
Advanced Python
Functional Programming
Decorators
● In Python, a decorator is a function that takes another function as input and
extends the behavior of the latter function (function that is passed as an
argument to the decorator) without modifying its source code.
● A decorator allows you to modify or add functionality to a function dynamically
at runtime.
Advanced Python
Functional Programming
Example 00:
def decorator(func): def greet():
def wrapper(): print("Hello, world!")
print("Before function result = decorator(greet)
execution")
# result() # Equivalent to calling wrapper()
func()
result()
print("After function execution")
return wrapper
Advanced Python
Functional Programming
Example 01:
def decorator(func): @decorator
def wrapper():
def greet():
print("Before function execution")
print("Hello, world!")
func()
greet()
print("After function execution")
return wrapper
Advanced Python
Functional Programming
Example 02: Decorator that converts the result to uppercase
def uppercase_decorator(func): @uppercase_decorator
def wrapper(): def greet():
result = func() return "Hello, world!"
return [Link]() # result = uppercase_decorator(greet)
return wrapper # print(result())
print(greet()) # Output: "HELLO, WORLD!"