Unit 5
Python Iterators
An iterator is an object that contains a countable number of values. An iterator is an object that
can be iterated upon, meaning that you can traverse through all the values.
Iterators are everywhere in Python. They are elegantly implemented within for loops,
comprehensions, generators etc. but hidden in plain sight. Iterator in Python is simply
an object that can be iterated upon. An object which will return data, one element at a time.
Technically speaking, Python iterator object must implement two special
methods, __iter__() and __next__(), collectively called the iterator protocol.
An object is called iterable if we can get an iterator from it. Most of built-in containers in
Python like: list, tuple, string etc. are iterables.
The iter() function (which in turn calls the __iter__() method) returns an iterator from them.
Iterator vs Iterable
Lists, tuples, dictionaries, and sets are all iterable objects. They are iterable containers which you
can get an iterator from. All these objects have a iter() method which is used to get an iterator:
Example
Return an iterator from a tuple, and print each value:
mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)
print(next(myit))
print(next(myit))
print(next(myit))
Even strings are iterable objects, and can return an iterator:
Example
Strings are also iterable objects, containing a sequence of characters:
mystr = "banana"
myit = iter(mystr)
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
Looping Through an Iterator
We can also use a for loop to iterate through an iterable object:
Example
Iterate the values of a tuple:
mytuple = ("apple", "banana", "cherry")
for x in mytuple:
print(x)
Example
Iterate the characters of a string:
mystr = "Rakesh"
for x in mystr:
print(x)
The for loop actually creates an iterator object and executes the next() method for each loop.
Create an Iterator
To create an object/class as an iterator you have to implement the
methods __iter__() and __next__() to your object. The __iter__() method acts similar, you can
do operations (initializing etc.), but must always return the iterator object itself.
The __next__() method also allows you to do operations, and must return the next item in the
sequence.
Example
Create an iterator that returns numbers, starting with 1, and each sequence will increase by one
(returning 1,2,3,4,5 etc.):
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
Stop Iteration
The example above would continue forever if you had enough next() statements, or if it was used
in a for loop. To prevent the iteration to go on forever, we can use the StopIteration statement.
In the __next__() method, we can add a terminating condition to raise an error if the iteration is
done a specified number of times:
Example
Stop after 20 iterations:
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
Recursion in Python
A function that calls itself is a recursive function. This method is used when a certain problem is
defined in terms of itself. Although this involves iteration, using an iterative approach to solve
such a problem can be tedious. The recursive approach provides a very concise solution to a
seemingly complex problem. It looks glamorous but can be difficult to comprehend!
The most popular example of recursion is the calculation of the factorial. Mathematically the
factorial is defined as: n! = n * (n-1)!
We use the factorial itself to define the factorial. Hence, this is a suitable case to write a recursive
function. Let us expand the above definition for the calculation of the factorial value of 5.
5! = 5 X 4!
5 X4 X 3!
5 X4 X 3 X 2!
5 X4 X 3 X 2 X 1!
5 X4 X 3 X 2 X 1
= 120
While we can perform this calculation using a loop, its recursive function involves successively
calling it by decrementing the number until it reaches 1. The following is a recursive function to
calculate the factorial.
Example: Recursive Function
def factorial(n):
if n == 1:
print(n)
return 1
else:
print (n,'*', end=' ')
return n * factorial(n-1)
The above recursive function can be called as below.
>>> factorial(5)
5*4*3*2*1
120
When the factorial function is called with 5 as argument, successive calls to the same function
are placed, while reducing the value of 5. Functions start returning to their earlier call after the
argument reaches 1. The return value of the first call is a cumulative product of the return values
of all calls.
Python Program for Fibonacci numbers
The Fibonacci numbers are the numbers in the following integer sequence.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ……..
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence
relation
Fn = Fn-1 + Fn-2
with seed values
F0 = 0 and F1 = 1.
Method 1 ( Use recursion ) :
# Function for nth Fibonacci number
def Fibonacci(n):
if n<0:
print("Incorrect input")
# First Fibonacci number is 0
elif n==1:
return 0
# Second Fibonacci number is 1
elif n==2:
return 1
else:
return Fibonacci(n-1)+Fibonacci(n-2)
# Driver Program
print(Fibonacci(9))
Output:
21
Method 3 ( Space Optimized):
# Function for nth fibonacci number - Space Optimisataion
# Taking 1st two fibonacci numbers as 0 and 1
def fibonacci(n):
a=0
b=1
if n < 0:
print("Incorrect input")
elif n == 0:
return a
elif n == 1:
return b
else:
for i in range(2,n):
c=a+b
a=b
b=c
return b
# Driver Program
print(fibonacci(9))
Output:
21
Tower of Hanoi
It is a mathematical puzzle where we have three rods and n disks. The objective of the puzzle is
to move the entire stack to another rod, obeying the following simple rules:
1) Only one disk can be moved at a time.
2) Each move consists of taking the upper disk from one of the stacks and placing it on top of
another stack i.e. a disk can only be moved if it is the uppermost disk on a stack.
3) No disk may be placed on top of a smaller disk.
Approach :
Take an example for 2 disks :
Let rod 1 = 'A', rod 2 = 'B', rod 3 = 'C'.
Step 1 : Shift first disk from 'A' to 'B'.
Step 2 : Shift second disk from 'A' to 'C'.
Step 3 : Shift first disk from 'B' to 'C'.
The pattern here is :
Shift 'n-1' disks from 'A' to 'B'.
Shift last disk from 'A' to 'C'.
Shift 'n-1' disks from 'B' to 'C'.
Image illustration for 3 disks :
Examples:
Input : 2
Output : Disk 1 moved from A to B
Disk 2 moved from A to C
Disk 1 moved from B to C
Input : 3
Output : Disk 1 moved from A to C
Disk 2 moved from A to B
Disk 1 moved from C to B
Disk 3 moved from A to C
Disk 1 moved from B to A
Disk 2 moved from B to C
Disk 1 moved from A to C
# Recursive Python function to solve tower of hanoi
def TowerOfHanoi(n , from_rod, to_rod, aux_rod):
if n == 1:
print "Move disk 1 from rod",from_rod,"to rod",to_rod
return
TowerOfHanoi(n-1, from_rod, aux_rod, to_rod)
print "Move disk",n,"from rod",from_rod,"to rod",to_rod
TowerOfHanoi(n-1, aux_rod, to_rod, from_rod)
# Driver code
n=4
TowerOfHanoi(n, 'A', 'C', 'B')
# A, C, B are the name of rods
Output:
Move disk 1 from rod A to rod B
Move disk 2 from rod A to rod C
Move disk 1 from rod B to rod C
Move disk 3 from rod A to rod B
Move disk 1 from rod C to rod A
Move disk 2 from rod C to rod B
Move disk 1 from rod A to rod B
Move disk 4 from rod A to rod C
Move disk 1 from rod B to rod C
Move disk 2 from rod B to rod A
Move disk 1 from rod C to rod A
Move disk 3 from rod B to rod C
Move disk 1 from rod A to rod B
Move disk 2 from rod A to rod C
Move disk 1 from rod B to rod C
Python Arrays
An array is a collection of items stored at contiguous memory locations. The idea is to store
multiple items of the same type together. This makes it easier to calculate the position of each
element by simply adding an offset to a base value, i.e., the memory location of the first element
of the array (generally denoted by the name of the array).
Creating a Array
Array in Python can be created by importing array module. array(data_type, value_list) is used
to create an array with data type and value list specified in its arguments.
# Python program to demonstrate creation of Array
# importing "array" for array creations
import array as arr
# creating an array with integer type
a = arr.array('i', [1, 2, 3])
# printing original array
print ("The new created array is : ", end =" ")
for i in range (0, 3):
print (a[i], end =" ")
print()
# creating an array with float type
b = arr.array('d', [2.5, 3.2, 3.3])
# printing original array
print ("The new created array is : ", end =" ")
for i in range (0, 3):
print (b[i], end =" ")
Output :
The new created array is : 1 2 3
The new created array is : 2.5 3.2 3.3
Adding Elements to a Array
Elements can be added to the Array by using built-in insert() function. Insert is used to insert one
or more data elements into an array. Based on the requirement, a new element can be added at
the beginning, end, or any given index of array. append() is also used to add the value mentioned
in its arguments at the end of the array.
# Python program to demonstrate Adding Elements to a Array
# importing "array" for array creations
import array as arr
# array with int type
a = arr.array('i', [1, 2, 3])
print ("Array before insertion : ", end =" ")
for i in range (0, 3):
print (a[i], end =" ")
print()
# inserting array using
# insert() function
a.insert(1, 4)
print ("Array after insertion : ", end =" ")
for i in (a):
print (i, end =" ")
print()
# array with float type
b = arr.array('d', [2.5, 3.2, 3.3])
print ("Array before insertion : ", end =" ")
for i in range (0, 3):
print (b[i], end =" ")
print()
# adding an element using append()
b.append(4.4)
print ("Array after insertion : ", end =" ")
for i in (b):
print (i, end =" ")
print()
Output :
Array before insertion : 1 2 3
Array after insertion : 1 4 2 3
Array before insertion : 2.5 3.2 3.3
Array after insertion : 2.5 3.2 3.3 4.4
Accessing elements from the Array
In order to access the array items refer to the index number. Use the index operator [ ] to access
an item in a array. The index must be an integer.
# Python program to demonstrate accessing of element from list
# importing array module
import array as arr
# array with int type
a = arr.array('i', [1, 2, 3, 4, 5, 6])
# accessing element of array
print("Access element is: ", a[0])
# accessing element of array
print("Access element is: ", a[3])
# array with float type
b = arr.array('d', [2.5, 3.2, 3.3])
# accessing element of array
print("Access element is: ", b[1])
# accessing element of array
print("Access element is: ", b[2])
Output :
Access element is: 1
Access element is: 4
Access element is: 3.2
Access element is: 3.3
Removing Elements from the Array
Elements can be removed from the array by using built-in remove() function but an Error arises
if element doesn’t exist in the set. Remove() method only removes one element at a time, to
remove range of elements, iterator is used.
pop() function can also be used to remove and return an element from the array, but by default it
removes only the last element of the array, to remove element from a specific position of the
array, index of the element is passed as an argument to the pop() method.
Note – Remove method in List will only remove the first occurrence of the searched element.
# Python program to demonstrate Removal of elements in a Array
# importing "array" for array operations
import array
# initializing array with array values
# initializes array with signed integers
arr = array.array('i', [1, 2, 3, 1, 5])
# printing original array
print ("The new created array is : ", end ="")
for i in range (0, 5):
print (arr[i], end =" ")
print ("\r")
# using pop() to remove element at 2nd position
print ("The popped element is : ", end ="")
print (arr.pop(2))
# printing array after popping
print ("The array after popping is : ", end ="")
for i in range (0, 4):
print (arr[i], end =" ")
print("\r")
# using remove() to remove 1st occurrence of 1
arr.remove(1)
# printing array after removing
print ("The array after removing is : ", end ="")
for i in range (0, 3):
print (arr[i], end =" ")
Output:
The new created array is : 1 2 3 1 5
The popped element is : 3
The array after popping is : 1 2 1 5
The array after removing is : 2 1 5
Slicing of a Array
In Python array, there are multiple ways to print the whole array with all the elements, but to
print a specific range of elements from the array, we use Slice operation. Slice operation is
performed on array with the use of colon(:). To print elements from beginning to a range use
[:Index], to print elements from end use [:-Index], to print elements from specific Index till the
end use [Index:], to print elements within a range, use [Start Index:End Index] and to print whole
List with the use of slicing operation, use [:]. Further, to print whole array in reverse order, use
[::-1].
# Python program to demonstrate slicing of elements in a Array
# importing array module
import array as arr
# creating a list
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a = arr.array('i', l)
print("Intial Array: ")
for i in (a):
print(i, end =" ")
# Print elements of a range
# using Slice operation
Sliced_array = a[3:8]
print("\nSlicing elements in a range 3-8: ")
print(Sliced_array)
# Print elements from a
# pre-defined point to end
Sliced_array = a[5:]
print("\nElements sliced from 5th "
"element till the end: ")
print(Sliced_array)
# Printing elements from
# beginning till end
Sliced_array = a[:]
print("\nPrinting all elements using slice operation: ")
print(Sliced_array)
Output :
Intial Array:
1 2 3 4 5 6 7 8 9 10
Slicing elements in a range 3-8:
array('i', [4, 5, 6, 7, 8])
Elements sliced from 5th element till the end:
array('i', [6, 7, 8, 9, 10])
Printing all elements using slice operation:
array('i', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Searching element in a Array
In order to search an element in the array we use a python in-built index() method. This function
returns the index of the first occurrence of value mentioned in arguments.
# Python code to demonstrate searching an element in array
# importing array module
import array
# initializing array with array values
# initializes array with signed integers
arr = array.array('i', [1, 2, 3, 1, 2, 5])
# printing original array
print ("The new created array is : ", end ="")
for i in range (0, 6):
print (arr[i], end =" ")
print ("\r")
# using index() to print index of 1st occurrenece of 2
print ("The index of 1st occurrence of 2 is : ", end ="")
print (arr.index(2))
# using index() to print index of 1st occurrenece of 1
print ("The index of 1st occurrence of 1 is : ", end ="")
print (arr.index(1))
Output:
The new created array is : 1 2 3 1 2 5
The index of 1st occurrence of 2 is : 1
The index of 1st occurrence of 1 is : 0
Updating Elements in a Array
In order to update an element in the array we simply reassign a new value to the desired index
we want to update.
# Python code to demonstrate how to update an element in array
# importing array module
import array
# initializing array with array values
# initializes array with signed integers
arr = array.array('i', [1, 2, 3, 1, 2, 5])
# printing original array
print ("Array before updation : ", end ="")
for i in range (0, 6):
print (arr[i], end =" ")
print ("\r")
# updating a element in a array
arr[2] = 6
print("Array after updation : ", end ="")
for i in range (0, 6):
print (arr[i], end =" ")
print()
# updating a element in a array
arr[4] = 8
print("Array after updation : ", end ="")
for i in range (0, 6):
print (arr[i], end =" ")
Output:
Array before updation : 1 2 3 1 2 5
Array after updation : 1 2 6 1 2 5
Array after updation : 1 2 6 1 8 5
Stack in Python
A stack is a linear data structure that stores items in a Last-In/First-Out (LIFO) or First-In/Last-
Out (FILO) manner. In stack, a new element is added at one end and an element is removed from
that end only. The insert and delete operations are often called push and pop.
The functions associated with stack are:
• empty() – Returns whether the stack is empty – Time Complexity : O(1)
• size() – Returns the size of the stack – Time Complexity : O(1)
• top() – Returns a reference to the top most element of the stack – Time Complexity : O(1)
• push(g) – Adds the element ‘g’ at the top of the stack – Time Complexity : O(1)
• pop() – Deletes the top most element of the stack – Time Complexity : O(1)
Implementation
There are various ways from which a stack can be implemented in Python. This article covers the
implementation of stack using data structures and modules from Python library.
Stack in Python can be implemented using following ways:
• list
Implementation using list
Python’s built-in data structure list can be used as a stack. Instead of push(), append() is used to
add elements to the top of stack while pop() removes the element in LIFO order.
Unfortunately, list has a few shortcomings. The biggest issue is that it can run into speed issue as
it grows. The items in list are stored next to each other in memory, if the stack grows bigger than
the block of memory that currently hold it, then Python needs to do some memory allocations.
This can lead to some append() calls taking much longer than other ones.
# Python program to demonstrate stack implementation using list
stack = []
# append() function to push
# element in the stack
stack.append('a')
stack.append('b')
stack.append('c')
print('Initial stack')
print(stack)
# pop() fucntion to pop
# element from stack in
# LIFO order
print('\nElements poped from stack:')
print(stack.pop())
print(stack.pop())
print(stack.pop())
print('\nStack after elements are poped:')
print(stack)
# uncommenting print(stack.pop())
# will cause an IndexError
# as the stack is now empty
Output:
Initial stack
['a', 'b', 'c']
Elements poped from stack:
c
b
a
Stack after elements are poped:
[]
Traceback (most recent call last):
File "/home/2426bc32be6a59881fde0eec91247623.py", line 25, in <module>
print(stack.pop())
IndexError: pop from empty list