0% found this document useful (0 votes)
5 views27 pages

Unit-3 Functional Programming

The document discusses various programming paradigms supported by Python, including procedural, object-oriented, and functional programming, highlighting their characteristics and advantages. It emphasizes functional programming's use of first-class functions, pure functions, and recursion, along with practical examples of list and dictionary comprehensions. Additionally, the document covers closures, explaining their definition, characteristics, and benefits in Python programming.

Uploaded by

pesed99031
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views27 pages

Unit-3 Functional Programming

The document discusses various programming paradigms supported by Python, including procedural, object-oriented, and functional programming, highlighting their characteristics and advantages. It emphasizes functional programming's use of first-class functions, pure functions, and recursion, along with practical examples of list and dictionary comprehensions. Additionally, the document covers closures, explaining their definition, characteristics, and benefits in Python programming.

Uploaded by

pesed99031
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

A. PROGRAMMING PARADIGMS:
A programming paradigm is a general approach to developing software. There aren’t
usually fixed rules about what is or isn’t part of a particular paradigm, but, rather, there
are certain patterns, characteristics, and models that tend to be used. This is especially
true of Python since it supports several paradigms with no real dividing line between
them. Here are the paradigms available in Python.

1. PROCEDURAL PROGRAMMING:
Procedural programming is the most basic form of coding. Code is structured
hierarchically into blocks (such as if statements, loops, and functions). It is arguably the
simplest form of coding. However, it can be difficult to write and maintain large and
complex software due to its lack of enforced structure.

2. OBJECT-ORIENTED PROGRAMMING:
Object-oriented programming (OOP) structures code into objects. An object typically
represents a real item in the program, such as a file or a window on the screen, and it
groups all the data and code associated with that item within a single software structure.
Software is structured according to the relationships and interactions between different
objects. Since objects are encapsulated, with well-defined behavior, and capable of being
tested independently, it is much easier to write complex systems using OOP.

3. FUNCTIONAL PROGRAMMING:
Functional programming (FP) uses functions as the main building blocks. Unlike
procedural programming, the functional paradigm treats functions as objects that can be

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 1/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

passed as parameters, allowing new functions to be built dynamically as the program


executes.

Functional programming tends to be more declarative than imperative – your code


defines what you want to happen, rather than stating exactly how the code should do it.
Some FP languages don’t even contain constructs, such as loops or if statements.
However, Python is more general-purpose and allows you to mix programming styles
very easily.

CHARACTERISTICS OF FUNCTIONAL
PROGRAMMING:
1. Functions as first class objects, which means that you should be able to apply all the constructs
of using data, to functions as well.
2. Pure functions; there should not be any side-effects in them.
3. Ways and constructs to limit the use of for loops
4. Good support for recursion

Functions as first class object in Python:

Using functions as first class objects means to use them in the same manner that you use
data. So, You can pass them as parameters like passing a function to another function as an
argument.

In [ ]:
print("Output: ",list(map(int,["1","2","3"])))

NOTE: Functional programming decomposes a problem into set of functions. The


map(), filter() and reduce() function form a part of functional programming tools
that work on all list items. However, it is recommended to use list
comprehensions instead of these functions where possible.

B. COMPREHENSIONS IN PYTHON
1. LIST

2. DICTIONARY

3. SET
LIST COMPREHENSION:
1. PYTHON SUPPORTS COMPUTED LISTS CALLED LIST COMPREHENSIONS.
2. SYNTAX
List =[expression for variable in sequence]
3. Where the expression is evaluated once, for every item in the sequence.
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 2/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

4. List comprehension help programmers to create list in a concise way.


5. This is mainly beneficial to make new lists where each element is obtained by applying some
operations to each member of another sequence or iterable.
6. List comprehension is also used to create a subsequence of those elements that satisfy a certain
condition.

In [ ]:
#Create a list of cubes of first 5 natural numbers

cubes=[]
for i in range(0,5):
cubes.append(i**3)
print ("Cubes of first 5 Natural Numbers are: ",cubes)

In [ ]:
#Using List Comprehensions:

cu=[i**3 for i in range(0,5)]


print(cu)

1. WAP that creates a list of 10 random integers. Then create two list i.e, ODD LIST and EVEN LIST that has all the
values respectively.

In [ ]:
import random

number=[]
for i in range(10):
val=random.randint(1,100)
number.append(val)

print("ORiginal LISt: ",number)

even_L=[]
odd_L=[]

for i in range(len(number)):
if (number[i]) %2==0:
even_L.append(number[i])
else:
odd_L.append(number[i])

print("List of Even Numbers: ",even_L)


print("List of Odd Numbers: ",odd_L)

2. WAP to create a list of first 20 odd numbers using the list comprehension method

In [ ]:
odd=[2*i +1 for i in range(20)]
print(odd)

3. WAP to combine the value in two lists using list comprehension. Combine only those value which of a list that are
multiples of values in the first list.

In [ ]:
Result=([(x,y) for x in [10,20,30] for y in [35,40,55,60] if y%x==0 or x%y==0])
print(Result)

4. WAP to iterate through a string using loop.


localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 3/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

In [ ]:
letters = []

for letter in 'Computer Science':


letters.append(letter)

print(letters)

5. WAP to iterate through a string using list comprehensions.

In [ ]:
c_letters = [ letter for letter in 'Computer Science' ]
print( c_letters)

6. Addition of two matrices:

In [ ]:
#Addition of Matrix

M1=[[1,2,3],[4,5,6]]
M2=[[4,5,6],[7,8,9]]
S=[]

for i in range(len(M1)):
TR=[]
for j in range(len(M1[0])):
TR.append(M1[i][j] + M2[i][j])
S.append(TR)
print("Sum of Matrix is: ",S)

#print(M1[1][2])

6.b.Addition using List Comprehension

In [ ]:
M1=[[1,2,3],[4,5,6]]
M2=[[4,5,6],[7,8,9]]
S=[[M1[i][j] + M2[i][j] for j in range(len(M1[0]))] for i in range(len(M1))]
print(f" Sum of Matrix is: ",S)

7. WAP to transpose a given matrix

In [ ]:
#Transpose of Matrix
#Using nested loop

M=[[1,2,3],[4,5,7]]
T=[]
for i in range(len(M[0])):
TR=[]
for row in M:
TR.append(row[i])
T.append(TR)
print(T)

7.b. TRANSPOSE USING LIST COMPREHENSION:

In [ ]:
#Using list comprehension

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 4/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
M=[[1,2,3],[4,5,7]]
print([[row[i] for row in M] for i in range (len(M[0]))])

8. WAP to encrypt the message in which each alphabet is replaced by characater having 3 character difference in the
alphabetic order.

In [ ]:
def convert(ch):
ch=ch.lower()
conv_char=chr(ord(ch)+3)
if conv_char in 'abcdefghijklmnopqrstuvwxyz':
return conv_char
elif ord(conv_char)>122 and ord(conv_char)<126:
return chr(ord(conv_char)-26)
else:
return ch
msg=input("Enter the Message: ")
encrypt="".join(map(convert,msg))
print("Encrypted Message is: ",encrypt)

DICTIONARY COMPREHENSIONS:
1. Dictionaries are data types in Python which allows us to store data in key/value pair.
2. Key: Value pairs is required to create a dictionary. To get these key-value pairs using dictionary
comprehension the general statement of dictionary comprehension is as follows:
{key: value for var in iterable}
3. Dictionary comprehension is an elegant and concise way to create dictionaries.

1. WAP to generate numbers as keys and their squares as values within the range of 10.

In [ ]:
square_dict = dict()
for num in range(1, 11):
square_dict[num] = num*num
print(square_dict)

1.b. Using Dictionary Comprehension

In [ ]:
square_dict = {num: num*num for num in range(1, 11)}
print(square_dict)

2. WAP to create a dictionary from [a, b, c, d] and [1, 2, 3, 4].

In [ ]:
keys=[1,2,3,4]
values=['a','b','c','d']
d={key:value for key,value in zip(keys,values)}
print(d)
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 5/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

ZIP FUNCTION:
zip() is a built-in function that takes two or more sequences and zips them into a list of
tuples.The tuple thus, formed has one element from each sequence.

In [ ]:
Tup=(1,2,3,4,5)
List1=['a','b','c','d','e']

print(list((zip(Tup,List1))))

SET COMPREHENSION:
1. Set comprehensions are pretty similar to list comprehensions.
2. The only difference between them is that set comprehensions use curly brackets { }.

In [ ]:
mylist = [1, 2, 3, 4, 4, 5, 6, 6, 6, 7, 7]
myset = set()
# Using loop for constructing myset
for var in mylist:
if var % 2 == 0:
myset.add(var)
print("Set using for loop:",myset)

In [ ]:
# Using Set comprehensions
mylist = [1, 2, 3, 4, 4, 5, 6, 6, 6, 7, 7]
myset = {var for var in mylist if var % 2 == 0}
print("Set using set comprehensions:", myset)

C. CLOSURE:
PRE-REQUISITE FOR UNDERSTANDING CLOSURE:
1. NESTED FUNCTION : A function defined inside another function is called a nested function. Nested functions can
access variables of the enclosing scope.

2. NON-LOCAL VARIABLE: Non-Local variables are read-only by default and we must declare them explicitly as non-
local (using nonlocal keyword) in order to modify them.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 6/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

In [ ]:
def outer_func():
message = 'Hello'

def inner_func():
print(message)

return inner_func

NESTED FUNCTION:
1. In the inner_func, you're actually looking at :

The inner_func function itself


The free variable message with the value 'Hello'
NOTE: This combination of the inner_func function and the message variable is called the
closure.

NON-LOCAL VARIABLE IN NESTED FUNCTION:


Every variable has a certain scope. The scope is essentially that part of the code, where
the variable can be found and can also be accessed, if required.

In [9]:
#Example for non-local

def fun():
x = 5
print(x) # will print the value of x (5) and will not throw error

#print(x) # will throw NameError x is not defined

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 7/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

DEFINITION OF CLOSURE:
1. Closure in Python is an inner function object, a function that behaves like an object,
2. Closure remembers and has access to variables in the local scope in which it was created
even after the outer function has finished executing.
3. It can also be defined as a means of binding data to a function (linking / attaching the data with
the function so that they are together), without passing it as a parameter. Even if values in
enclosing scopes are not present in memory, a closure is a function object that
remembers those values.

THREE CHARACTERISTICS OF A PYTHON CLOSURE ARE:

1. It is a nested function.
2. It has access to a free variable in outer scope.
3. It is returned from the enclosing function.
4. Closure can be called a function object (as it is a function that behaves like an object) that
is capable of remembering values that are in enclosing scopes (such as the outer
functions) even if they are not present in memory.

NOTE: A free variable is a variable that is not bound in the local scope.

POINT TO PONDER: Python closure help avoiding the usage of global values and provide some
form of data hiding.

SUMMARY: A python closure isn't like a plain function. It allows the function to access those captured
variables through the closure’s copies of their values or references, even if the function is invoked
outside their scope.

In [ ]:
# Example : Nested function
# When a function is defined inside another function.
def outerfunc():
x = 10

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 8/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

def innerfunc():
print(x)
innerfunc()
outerfunc()

EXPLAINATION OF ABOVE CODE: inner() could read the varibale x which is non-local to it and it
must modify x we declare that it's non-local to inner().

In [ ]:
#Programme to elaborate Closure

def power(exponent):
def power_of(base):
return pow(base,exponent)
return power_of

square=power(2)
print(square(2))
print(square(3))
print(square(4))
print(square(5))
print(square(6))

cube=power(3)
print(cube(2))

In [6]:
#Closure

def divider(y):
def divide(x):
return x / y
return divide

d1=divider(3)
print(d1(9))

3.0

WHEN TO USE PYTHON CLOSURE AND THEIR BENEFITS:


1. Say you have a class that has two methods. In the Python programming language, a class
always has the init method. If you have just one more method besides it, an elegant solution
would be to use a closure, instead of a class. Why? This increases readability of the code and
even reduces work of the programmer. So, closures can be used to avoid the unnecessary
use of class.

1. Sometimes you might have variables in the global scope that are not used by more than one
function. Instead of declaring the variables in global scope, you must think of using a closure.
You can define them in the outer function and use them in the inner function. Closures can
also be used to avoid the use of global scope.

1. Have you thought about calling the inner function directly at any point of time? You cannot do
it! The only way to access the inner function is by calling the outer function. Data hiding is an

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 9/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

important use of closures.

D. ITERATOR IN PYTHON:
DEFINITION: An Iterable is an object that contains a sequence or countable values
that can be traversed upon. It is the object that is used to iterate over iterable
objects like lists, tuples, dicts, and sets.

ITERABLES: Iterables are objects that act as iterables containers to get the iterator.
Anything that you can loop over in Python is called an iterable. For an object to be
considered an iterable, it must have the iter() method.

ITERABLES IN PYTHON

ITERATOR PROTOCOL:
The iterator objects are required to support the following two methods, which together form the
iterator protocol:

1. iterator.iter(): Return the iterator object itself. This is required to allow both containers (also
called collections) and iterators to be used with the for and in statements.(PRESENT IN
ITERABLES)

2. iterator.next(): Return the next item from the container. If there are no more items, raise the
StopIteration exception.(PRESENT IN ITERATOR)

In [1]:
#Iterable 'A' directory containing __iter_() method

A=[1,2,3,4,5]
print(dir(A))

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 10/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem_


_', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getit
em__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '_
_iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__',
'__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'exten
d', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

In [2]:
# Iterator 'value' directory containing __next__() method

A=[1,2,3,4,5,6,7]
value=A.__iter__()
print(dir(value))

['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__


getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__l
e__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__red
uce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subcla
sshook__']

SEQUENCES: LIST,STRINGS,TUPLES

ITERABLES: DICTIONARY,FILE,OBJECTS,SETS

In [ ]:
numbers=[1,2,3,4,5,6,7,8,9,10]
strings="Class of Computer Science 2021"
tuples=("Car","Motorcycle","Bus","Aeroplane","Tanks")

They support efficient element access using integer indices via the getitem() special method
(indexing) and define a length() method that returns the length of the sequence.

Also, we can use the slicing technique on them.

In [7]:
# Element access using integer indices
numbers=[1,2,3,4,5,6,7,8,9,10]
strings="Class of Computer Science 2021"
tuples=("Car","Motorcycle","Bus","Aeroplane","Tanks")

print(numbers[4])
print(strings[0])
print(tuples[-5])

5
C
Car

In [12]:
# Slicing the sequences

numbers=[1,2,3,4,5,6,7,8,9,10]
strings="Class of Computer Science 2021"
tuples=("Car","Motorcycle","Bus","Aeroplane","Tanks")

print(numbers[:2])

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 11/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
print(strings[6:])
print(tuples[2:])

[1, 2]
of Computer Science 2021
('Bus', 'Aeroplane', 'Tanks')

ALERT: Many things in Python are iterables, but not all


of them are sequences. Dictionaries, file objects, sets,
and generators are all iterables, but none of them is a
sequence.
In [14]:
my_set = {2, 3, 5}
my_dict = {"name": "Ventsislav", "age": 24}
#my_file = open("file_name.txt")
squares = (n**2 for n in my_set)

index = 0
numbers = {1, 2, 3, 4, 5}
while index < len(numbers):
print(numbers[index])
index += 1

---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_12524/68743835.py in <module>
8 numbers = {1, 2, 3, 4, 5}
9 while index < len(numbers):
---> 10 print(numbers[index])
11 index += 1

TypeError: 'set' object is not subscriptable

ITER() AND NEXT() METHODS IN PYTHON:


1. The Python iter() function returns an iterator for the given object. The iter() function creates
an object which can be iterated one element at a time.These objects are useful when
coupled with loops like for loop, while loop.

SYNTAX: iter(object,sentinel)

1. iter() PARAMETERS: The iter function takes two parameters


object - object whose iterator has to be created (can be sets, tuples, etc.)
sentinel (optional) - special value that is used to represent the end of a sequence

1. RETURNS VALUE FROM ITER():


The iter() function returns an iterator object for the given object.
If the user-defined object doesn't implement iter(), and next() or getitem(), the TypeError
exception is raised.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 12/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

If the sentinel parameter is also provided, iter() returns an iterator until the sentinel
character isn't found.

In [18]:
# Program to demonstrate __iter__() methods

x=[1,2,3,4,5,6,7,8,]

value=x.__iter__()
print(value)
print(dir(x)) # to check whether __iter__ is present or not in iterable.

<list_iterator object at 0x0000018C2467B940>


['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem_
_', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getit
em__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '_
_iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__',
'__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'exten
d', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

next() method:
1. Python next() function returns the next item of an iterator. It always return the current
value of the iterable and updates the state to the next value.

SYNATX : next (iterator,default)

1. next() Parameters:

iterator - next() retrieves next item from the iterator.


default (optional) - this value is returned if the iterator is exhausted (there is no next
item).
2. next() Return Value:

The next() function returns the next item from the iterator.
If the iterator is exhausted, it returns the default value passed as an argument.
If the default parameter is omitted and the iterator is exhausted, it raises the StopIteration
exception.

In [19]:
# Program to demonstrate __next__() method

x=[1,2,3,4,5,6,7,8,]

value=x.__iter__()
item1=value.__next__()
print(item1)
item2=value.__next__()
print(item2)
item3=value.__next__()
print(item3)
#print(dir(value)) # to check whether __super__ is present or not in iterable.

1
2
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 13/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
3

In [27]:
#Note: Python has an elegant way to call __iter__() simply with the iter() function and
#function.

numbers = [1, 2, 3]
value = iter(numbers)

item1 = next(value)
print(item1)

item2 = next(value)
print(item2)

item3 = next(value)
print(item3)

item4 = next(value)
print(item4)

1
2
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_12524/40794909.py in <module>
11 print(item3)
12
---> 13 item4 = next(value)
14 print(item4)

StopIteration:

In [20]:
st="python"
it2=iter(st)
print(next(it2))
print(it2.__next__())
print(next(it2))
print(next(it2))
print(next(it2))
print(next(it2))
print(next(it2))

p
y
t
h
o
n
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9684/862733828.py in <module>
7 print(next(it2))
8 print(next(it2))
----> 9 print(next(it2))

StopIteration:

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 14/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

In [32]: #Passing default value to next()

list1 = [1, 2, 3, 4, 5]

# converting list to iterator


list1 = iter(list1)

print(list1)
print(next(list1, -1))
print(next(list1, -1))
print(next(list1, -1))
print(next(list1, -1))
print(next(list1, -1))
print(next(list1, -1))
print(next(list1, -1))

<list_iterator object at 0x0000018C24670EB0>


1
2
3
4
5
-1
-1

How do ''for-in " loops work in Python ?


"for-in" was just syntactic sugar for a simple "while loop":

Lets construct our own for loop:


We need following things for this :

1. Create an iterator from the given iterable.


2. Repedeatly get the next item from the iterator.
3. Execute the wanted action.
4. Stop the looping, if we got a StopIteration exception when we’re trying to get the next item.

In [30]:
def custom_for_loop(iterable, action_to_do):
iterator = iter(iterable)
done_looping = False
while not done_looping:
try:
item = next(iterator)
except StopIteration:
done_looping = True
else:
action_to_do(item)

numbers = [1, 2, 3, 4, 5]
custom_for_loop(numbers, print)

1
2
3
4
5
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 15/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

Here's how this code works:


1. First, we have created an iterator object from a list using iter_obj(iterator) =
iter(iterable(numbers)).
2. Then, we have created an infinite while loop.
3. Inside the loop, we have used the next method to return the next element in the sequence
item = next(iter_obj)) and printed it. We have put this code inside the try block.
4. When all the items of the iterator are iterated, the try block raises the StopIteration exception,
and the except block catches it and breaks the loop.

NOTE: In fact, this is exactly how for loops work behind the scene. A for loop internally creates an
iterator object, and iterates over it calling the next method until a StopIteration exception is
encountered.

In [31]:
# The above code is equivalent to:

numbers = [1, 2, 3, 4, 5]

for element in numbers:


print(element)

1
2
3
4
5

CREATING CUSTOMS ITERATORS:


In some cases, we may want to create a custom iterator. We can do that by defining a class that
has init, next, and iter methods.

1. Create an iterator that returns numbers, starting with 1, and each sequence will increase by one (returning 1,2,3,4,5
etc.)

In [15]:
class Numbers:

def __iter__(self):
self.a=1
return self

def __next__(self):
x=self.a
self.a += 1
return x

obj=Numbers()
iter_obj=iter(obj)
print(next(iter_obj))
print(next(iter_obj))
print(next(iter_obj))

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 16/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
1
2
3
2. CUSTOM ITERATOR FOR SQUARED NUMBERS:

In [21]:
#iterator for squared numbers
class Sq:
def __init__(self):
self.a=0
def __iter__(self):
return self
def __next__(self):
if self.a<=9:
self.a=self.a+1
return self.a**2
else:
raise StopIteration
for i in Sq():
print(i)

1
4
9
16
25
36
49
64
81
100
3. CUSTOM ITERATORS FOR FIBONACCI SERIES

In [22]:
#Program to create iterator for fibbonacci series
class fib:
def __init__(self,stop):
self.a=-1
self.b=1
self.n=0
self.stop=stop
def __iter__(self):
return self
def __next__(self):
self.n+=1
if self.n<=self.stop:
c=self.a+self.b
self.a=self.b
self.b=c
return c
else:
raise StopIteration
#for i in fib(10):
# print(i,end=',')
f1=fib(5)
fit=iter(f1)
print(next(fit))
print(next(fit))
print(next(fit))
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 17/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
print(next(fit))
print(next(fit))
print(next(fit))

0
1
1
2
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9684/3945298406.py in <module>
26 print(next(fit))
27 print(next(fit))
---> 28 print(next(fit))

~\AppData\Local\Temp/ipykernel_9684/3945298406.py in __next__(self)
16 return c
17 else:
---> 18 raise StopIteration
19 #for i in fib(10):
20 # print(i,end=',')

StopIteration:

In [23]:
#iterator to create our own range function
class myrange:
def __init__(self,sp,s=0,st=1):
self.s=s
self.sp=sp
self.st=st
def __iter__(self):
return self
def __next__(self):
if self.s<self.sp:
x=self.s
self.s=self.s+self.st
return x
else:
raise StopIteration
for i in myrange(10,2,2):
print(i)

2
4
6
8

4. Create a custom iterator class that generate numbers between min value and max value.

In [9]:
class generate_numbers:
def __init__(self, min_value, max_value):
self.current = min_value
self.high = max_value

def __iter__(self):
return self

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 18/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

def __next__(self):
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1

numbers = generate_numbers(40, 50)


print(type(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))
print(next(numbers))

<class '__main__.generate_numbers'>
40
41
42
43
44
45
46
47
48
49
50
5. Create a custom iterator object to generate a sequence of even numbers such as 2, 4, 6, 8 and so on.

In [1]:
class Even:
def __init__(self, max):
self.n = 2
self.max = max

def __iter__(self):
return self

def __next__(self):
if self.n <= self.max:
result = self.n
self.n += 2
return result
else:
raise StopIteration

numbers = Even(10)

print(next(numbers))
print(next(numbers))
print(next(numbers))

2
localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 19/27
6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
4
6

BENEFITS OF ITERATORS
1. Iterators are powerful tools when dealing with a large stream of data.
2. If we used regular lists to store these values, our computer would run out of memory.
3. With iterators, however, we can save resources as they return only one element at a time. So, in
theory, we can deal with infinite data in finite memory.

E. GENERATORS IN PYTHON:
WHY GENERATORS?
There is a lot of work in building an iterator in Python. We have to implement a class with
iter() and next()_ method, keep track of internal states, and raise StopIteration when
there are no values to be returned.

This is both lengthy and counterintuitive. Generator comes to the rescue in such
situations.

Python generators are a simple way of creating iterators. All the work we mentioned
above are automatically handled by generators in Python.

DEFINITION: Python's generator functions are used to create


iterators(which can be traversed like list, tuple) and return a
traversal object. It helps to transverse all the items one at a time
present in the iterator.
Generator functions are defined as the normal function, but to identify the difference between
the normal function and generator function is that in the normal function, we use the
return keyword to return the values, and in the generator function, instead of using the
return, we use yield to execute our iterator.

In [2]:
def fun():
print("function starts")
print("next statement of function")
return 0
fun()

function starts
next statement of function
0
Out[2]:

In [3]:
fun()

function starts
next statement of function
0
Out[3]:

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 20/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

In [30]:
def fun1():
i=0
print("function begins")
yield i
i+=1
print("function continue")
yield i
i+=1
print("function terminates")
yield i

obj=fun1()

print(type(fun1))
print(type(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))

<class 'function'>
<class 'generator'>
function begins
0
function continue
1
function terminates
2
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5404/1620009420.py in <module>
18 print(next(obj))
19 print(next(obj))
---> 20 print(next(obj))

StopIteration:

In [15]:
obj2=fun1()
for i in obj2:
print(i)

function begins
0
function continue
1
function terminates
2

In [1]:
# A Generator function with for loop

def gen_fun():
yield 10
yield 20
yield 30

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 21/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING
for i in gen_fun():
print(i)

10
20
30
NOTE: Generator function use ''yield'' keyword instead of ''return'' and it will return a value whenever it is called.

DIFFERENCE BETWEEN GENERATOR FUNCTION AND NORMAL


FUNCTION:
GENERATOR FUNCTION:
1. Generator function contains one or more yield statements.

1. When called, it returns an object (iterator) but does not start execution immediately.

1. Methods like iter() and next() are implemented automatically. So we can iterate through the
items using next().

1. Once the function yields, the function is paused and the control is transferred to the caller.

1. Local variables and their states are remembered between successive calls.

1. Finally, when the function terminates, StopIteration is raised automatically on further calls.

DIFFERENCE BETWEEN YIELD AND RETURN STATEMENT:


YIELD STATEMENT:
1. Yield is generally used to convert a regular Python function into a generator.

1. It replace the return of a function to suspend its execution without destroying local
variables.

1. It is used when the generator returns an intermediate result to the caller.

1. Code written after yield statement execute in next function call.

1. It can run multiple times.

1. Yield statement function is executed from the last state from where the function get paused.

RETURN STATEMENT:
1. Return is generally used for the end of the execution and “returns” the result to the caller
statement.

1. It exits from a function and handing back a value to its caller.

1. It is used when a function is ready to send a value.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 22/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

1. while, code written after return statement wont execute.

1. It only runs single time.

1. Every function calls run the function from the start.

In [31]:
# Programme to demonstrate yield keyword

# Use of yield
def printresult(String) :
for i in String:
if i == "a":
yield i

# initializing string
String = "Agapastala"
ans = 0
print ("The number of 'a' in word is : ", end = "" )
String = String.strip()

for j in printresult(String):
ans = ans + 1

print (ans)

The number of 'a' in word is : 4

In [21]:
# Program to show return statement
class Test:
def __init__(self):
self.str = "Agapastala"
self.x = "Ranbir Kapoor"

# This function returns an object of Test


def fun():
return Test()

# Driver code to test above method


t = fun()
print(t.str)
print(t.x)

Agapastala
Ranbir Kapoor

HOW TO CREATE GENERATOR FUNCTIONS??


1. USING YIELD STATEMENT

2. USING PYTHON GENERATOR EXPRESSION

PYTHON GENERATOR EXPRESSION:


1. Generator Expression is a short-hand form of a generator function. Python provides an
easy and convenient way to implement a Generator Expression.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 23/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

2. According to its definition, they are similar to lambda function as lambda function is an
anonymous function, and generator functions are anonymous.

3. But when it comes to implementation, they are different, they are implemented similarly to list
comprehension does, and the only difference in implementation is, instead of using square
brackets('[]'), it uses round brackets('()').

4. The main and important difference between list comprehension and generator expression is
list comprehension returns a list of items, whereas generator expression returns an
iterable object.

In [25]:
# Creation of generator using generator expression.

x = 10
gen = (i for i in range(x) if i % 2 == 0) #(i for i in range(x) if i % 2 == 0) in this

list_ = [i for i in range(x) if i % 2 == 0] #and next is the for loop, followed by the

print(gen)
print(list_)
for j in gen:
print(j)

<generator object <genexpr> at 0x000001E6491C7970>


[0, 2, 4, 6, 8]
0
2
4
6
8

In [22]:
# Generator function

def even_generator():
n = 0

n += 2
yield n

n += 2
yield n

n += 2
yield n

numbers = even_generator()

print(next(numbers))
print(next(numbers))
print(next(numbers))

2
4
6

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 24/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

EXPLANATION OF THE ABOVE CODE:


First, we have created a generator function that has three yield statements. When we call this
generator, it returns an iterator object.

Then, we have called the next() method to retrieve elements from this iterator. The first yield
returns the value of n = 2.

The difference between return and yield is that the return statement terminates the function
completely while the yield statement pauses the function saving all its states for next successive
calls.

So, when we call yield for the second and third time, we get 4 and 6 respectively.

In [32]:
def even_generator(max):
n = 2

while n <= max:


yield n
n += 2

numbers = even_generator(4)

print(next(numbers))
print(next(numbers))
#print(next(numbers))

#Notice how we have never explicitly defined the __iter__() method, __next__() method,
#They are handled implicitly by generators making our program much simpler and easier t

2
4
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_5404/3768536747.py in <module>
10 print(next(numbers))
11 print(next(numbers))
---> 12 print(next(numbers))
13
14

StopIteration:

GENERATORS WITH INFINITE STREAM OF DATA:


1. Iterators and generators are generally used to handle a large stream of data--
theoretically even an infinite stream of data. These large streams of data cannot be stored
in memory at once. To handle this, we can use generators to handle only one item at a
time.

let's build a generator to produce an infinite stream of fibonacci numbers. The fibonacci
series is a series where the next element is the sum of the last two elements.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 25/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

In [26]: def infinite():


n = 0
while True:
yield n
n += 1

for i in infinite():
if i%4 == 0:
continue
elif i == 13:
break
else:
print(i)

1
2
3
5
6
7
9
10
11

In [ ]:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,.....

In [ ]:
def generate_fibonacci():
n1 = 0
yield n1

n2 = 1
yield n2

while True:
n1, n2 = n2, n1 + n2
yield n2

seq = generate_fibonacci()

print(next(seq))
print(next(seq))
print(next(seq))
print(next(seq))
print(next(seq))

NOTE: If we had used a for loop and a list to store this infinite series, we would have run out of
memory.However, with generators, we can keep accessing these items for as long as we want. It is
because we are just dealing with one item at a time.

USE OF GENERATORS FUNCTION:


1. EASY TO IMPLEMENT: Generator functions are easy to implement as compared with iterators.
In iterators, we have to implement iter(), next() function to make our iterator work.

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 26/27


6/8/22, 10:33 AM UNIT-3 FUNCTIONAL PROGRAMMING

1. MEMORY EFFICIENT: Generator Functions are memory efficient, as they save a lot of memory
while using generators. A normal function will return a sequence of items, but before giving the
result, it creates a sequence in memory and then gives us the result, whereas the generator
function produces one output at a time.

1. INFINITE SEQUENCE: We all are familiar that we can't store infinite sequences in a given
memory. This is where generators come into the picture. As generators can only produce one
item at a time, so they can present an infinite stream of data/sequence.

1. Create a generator function to generate an infinite stream of odd numbers and print the
first 10 elements.

In [ ]:
def generate_odd():
n = 1
while True:
yield n
n += 2

odd_generator = generate_odd()

for num in range(10):


print(next(odd_generator))

localhost:8888/nbconvert/html/UNIT-3 FUNCTIONAL PROGRAMMING.ipynb?download=false 27/27

You might also like