0% found this document useful (0 votes)
8 views206 pages

Python Notes 2

The document explains the concept of sequences in Python, focusing on lists and tuples. It highlights that lists can store different types of elements, allowing for more versatility compared to arrays, and discusses operations such as indexing, slicing, and modifying list contents. Additionally, it covers creating lists using the range() function, accessing elements with loops, and performing basic operations like concatenation and repetition.

Uploaded by

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

Python Notes 2

The document explains the concept of sequences in Python, focusing on lists and tuples. It highlights that lists can store different types of elements, allowing for more versatility compared to arrays, and discusses operations such as indexing, slicing, and modifying list contents. Additionally, it covers creating lists using the range() function, accessing elements with loops, and performing basic operations like concatenation and repetition.

Uploaded by

Richa Bandal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

LISTS AND TUPLES

A sequence is a datatype that represents a group of elements. The purpose


of any sequence is to store and process a group of elements.

In Python, strings, lists, tuples and dictionaries are very important sequence
datatypes. All sequences allow some common operations like indexing and
slicing.

List

A list is similar to an array that consists of a group of elements or items.

Just like an array, a list can store elements.

But, there is one major difference between an array and a list.

An array can store only one type of elements whereas a list can store
different types of elements.

Hence lists are more versatile and useful than an array. Perhaps lists are the
most used datatype in Python programs.

In our daily life, we do not have elements of the same type. For example, we
take marks of a student in 5 subjects: 50, 55, 62, 74, 66 These are all
belonging to the same type, i.e. integer type. Hence, we can represent such
elements as an array. But, we need other information about the student, like
his roll number, name, gender along with his marks. So, the information
looks like this: 10, Venu gopal, M, 50, 55, 62, 74, 66 Here, we have different
types of data. Roll number (10) is an integer. Name (‘Venu gopal’) is a string.
Gender (‘M’) is a character and the marks (50, 55, 62, 74, 66) are again
integers. In daily life, generally we have this type of information that is to be
stored and processed. This type of information cannot be stored in an array
because an array can store only one type of elements. In this case, we need
to go for list datatype.

A list can store different types of elements. To store the student’s information
discussed so far, we can create a list as:

student = [10, 'Venu gopal', 'M', 50, 55, 62, 74, 66]

We can create an empty list without any elements by simply writing empty
square braces as:

e_lst = [] # this an empty list


Thus we can create a list by embedding the elements inside a pair of square
braces []. The elements in the list should be separated by a comma ( , ). To
view the elements of a list as a whole, we can simply pass the list name to
the print() function as: print(student)

The list appears as given below:

[10, 'Venu gopal', 'M', 50, 55, 62, 74, 66]

Indexing and slicing operations are commonly done on lists. Indexing


represents accessing elements by their position numbers in the list. The
position numbers start from 0 onwards and are written inside square braces
as: student[0], student[1], etc... It means, student[0] represents 0th element,
student[1] represents 1st element and so forth. For example, to print the
student’s name, we can write:

print(student[1])

Slicing represents extracting a piece of the list by mentioning starting and


ending position numbers. The general format of slicing is:

[start: stop: stepsize].

By default, ‘start’ will be 0, ‘stop’ will be the last element and ‘stepsize’ will
be 1. For example, student[0:3:1] represents a piece of the list containing
0th to 2nd elements. print(student[0:3:1])

The elements are given below: [10, 'Venu gopal', 'M']

We can also write the above statement as: print(student[:3:]) .Here, since we
did not mention the starting element position, it will start at 0 and stepsize
will be taken as 1. Suppose, we do not mention anything in slicing, then the
total list will be extracted as: print(student[::])

It displays the output as: [10, 'Venu gopal', 'M', 50, 55, 62, 74, 66] Apart from
indexing and slicing, the 5 basic operations: finding length, concatenation,
repetition, membership and iteration operations can be performed on lists
and other sequences like strings, tuples or dictionaries.

In the following program, we are creating lists with different types of


elements and also displaying the list elements.

Program 1: A Python program to create lists with different types of


elements.

# a general way to create lists


# create a list with integer numbers
num = [10, 20, 30, 40, 50]
print('Total list= ', num) # display total list
print('First= %d, Last= %d' % (num[0], num[4])) # display first and
# last elements
# create a list with strings
names = ["Raju", "Vani", "Gopal", "Laxmi"]
# display entire list
print('Total list= ', names)
# display first and last elements
print('First= %s, Last= %s' % (names[0], names[3]))

# create a list with different elements


x = [10, 20, 10.5, 2.55, "Ganesh", 'Vishnu']
print('Total list= ', x) # display entire list
print('First= %d, Last= %s' % (x[0], x[5])) # display first and last elements

Output: C:\>python [Link]


Total list= [10, 20, 30, 40, 50]
First= 10, Last= 50
Total list= ['Raju', 'Vani', 'Gopal', 'Laxmi']
First= Raju, Last= Laxmi
Total list= [10, 20, 10.5, 2.55, 'Ganesh', 'Vishnu']
First= 10, Last= Vishnu

Creating Lists using range() Function


We can use range() function to generate a sequence of integers which can
be stored in a
list. The format of the range() function is:
range(start, stop, stepsize)
If we do not mention the ‘start’, it is assumed to be 0 and the ‘stepsize’ is
taken as 1. The
range of numbers stops one element prior to ‘stop’. For example,
range(0, 10, 1)

This will generate numbers from 0 to 9, as: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].


Consider another
example:
range(4,9,2)
The preceding statement will generate numbers from 4th to 8th in steps of 2,
i.e. [4, 6, 8].
In fact, the range() function does not return list of numbers. It returns only
range class
object that stores the data about ‘start’, ‘stop’ and ‘stepsize’. For example, if
we write:
print(range(4,9,2))
The preceding statement will display:
range(4, 9, 2) # this is the object given by range()
This range object should be used in for loop to get the range of numbers
desired by the
programmer. For example:
for i in range(4,9,2):
print(i)
The preceding statement will display 4, 6, 8. Hence, we say range object is
‘iterable’, that
is, suitable as a target for functions and loops that expect something from
which they can
obtain successive items.
For example, range object can be used in for loops to display the numbers, or
with list() function to create a list. In the following example, the range()
function is used in the list() function to create a list:
lst = list(range(4, 9, 2))
print(lst)
The list is shown below:
[4, 6, 8]
If we are not using the list() function and using range() alone to create a list,
then we will
have only range class object returned by the range() function. For example,
lst = range(4, 9, 2)
print(lst)
The preceding statements will give the following output:
range(4, 9, 2) # this is not a list, it is range object
In this case, using a loop like for or while is necessary to view the elements
of the list. For
example,
for i in lst:
print (i)
will display 4, 6, 8. In Program 2, we are showing examples of how to create
lists using
the range() function. In this program, we are not using the list() function.

Program 2: A Python program to create lists using range() function.


# creating lists using range() function
# create a list with 0 to 9 consecutive integer numbers
list1 = range(10)
for i in list1: # display element by element
print(i, ', ', end='')
print() # throw cursor to next line
#create list with integers from 5 to 9
list2 = range(5, 10)
for i in list2:
print(i, ', ', end='')
print()
# create a list with odd numbers from 5 to 9
list3 = range(5, 10, 2) # step size is 2
for i in list3:
print(i, ', ', end='')
Output:
C:\>python [Link]
0,1,2,3,4,5,6,7,8,9,
5,6,7,8,9,
5,7,9,
We can use while loop or for loop to access elements from a list. The len()
function is
useful to know the number of elements in the list. For example, len(list) gives
total
number of elements in the list. The following while loop retrieves starting
from 0th to the
last element of the list:
i=0
while i<len(list): # repeat from 0 to length of list
print(list[i])
i=i+1
Observe the len(list) function in the while condition as: while i<len(list). This
will return
the total number of elements in the list. If the total number of elements is
‘n’, then the
condition will become: while(i<n). It means i values are changing from 0 to n-
1. Thus this
loop will display all elements of the list from 0 to n-1.
Another way to display elements of a list is by using a for loop, as:
for i in list: # repeat for all elements
print(i)
Here, ‘i’ will assume one element at a time from the list and hence if we
display ‘i’ value, it
will display the elements one by one. In Program 3, we are showing how to
access the
elements of a list using a while loop and a for loop.
Program
Program 3: A Python program to access list elements using loops.
# displaying list elements using while and for loops
list = [10,20,30,40,50]
print('Using while loop')
i=0
while i<len(list): # repeat from 0 to length of list
print(list[i])
i=i+1
print('Using for loop')
for i in list: # repeat for all elements
print(i)
Output:
C:\>python [Link]
Using while loop
10
20
30
40
50
Using for loop
10
20
30
40
50
Updating the Elements of a List
Lists are mutable. It means we can modify the contents of a list. We can
append, update
or delete the elements of a list depending upon our requirements.
Appending an element means adding an element at the end of the list. To
append a new
element to the list, we should use the append() method. In the following
example, we are
creating a list with elements from 1 to 4 and then appending a new element
9.
lst = list(range(1,5)) # create a list using list() and range()
print(lst)
The preceding statements will give the following output:
[1, 2, 3, 4]
Now, consider the following statements:
[Link](9) # append a new element to lst
print(lst)
The preceding statements will give the following output:
[1, 2, 3, 4, 9]
Updating an element means changing the value of the element in the list.
This can be
done by accessing the specific element using indexing or slicing and
assigning a new
value. Consider the following statements:
lst[1]= 8 # update 1st element of lst
print(lst)
The preceding statements will give:
[1, 8, 3, 4, 9]

Consider the following statements:


lst[1:3] = 10, 11 #update 1st and 2nd elements of lst
print(lst)
The preceding statements will give:
[1, 10, 11, 4, 9]
Deleting an element from the list can be done using ‘del’ statement. The del
statement
takes the position number of the element to be deleted.
del lst[1] # delete 1st element from lst
print(lst)
Now, the list appears as:
[1, 11, 4, 9]
We can also delete an element using the remove() method. In this method,
we should
pass the element to be deleted.
[Link](11) # delete 11 from lst
print(lst)
Now, the list appears as:
[1, 4, 9]
Let’s write a program to retrieve the elements of a list in reverse order. This
can be done
easily by using the reverse() method, as:
[Link]()
This will reverse the order of elements in the list and the reversed elements
are available
in the list. Suppose, we do not have the reverse() method, then how can we
develop logic
to display the elements in reverse order? This can be done using while loop
and accessing
the elements in reverse order. For example, let’s assume a list with 5
elements. The
positions of these elements can be specified using indexing, as: list[0] to
list[4]. To display
in reverse order, we should follow the order: list[4] to list[0]. The following
while loop can
be used to display the list in reverse order:
while i>=0: # i represents initially 4.
print(list[i]) # display from 4th to 0th elements
i-=1 # decrease the position every time.
In the previous code, ‘i’ starts with a value ‘n-1’ where ‘n’ represents the
number of
elements of the list. Thus if the list has 5 elements, i would start from 4th
element
onwards till 0th element.
Another way to access elements in reverse order is by using negative
indexing. When we
write list[-1], it represents the last element. List[-2] represents 2nd element
from the end.
Hence, we should display from list[-1] to list[-5] so that the list will be
displayed in
reverse order.
i=-1 # last element
while i>=-5: # display from -1th to -5th elements
print(days[i])
i-=1 # decrease the position every time.

This logic is shown in Program 4 where we are displaying the elements of a


list in reverse
order using while loop in two different ways.

Program 4: A Python program to display the elements of a list in reverse


order.
# displaying list elements in reverse order
days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday']
print('\nIn reverse order: ')
i=len(days)-1 # i will be 4
while i>=0:
print(days[i]) # display from 4th to 0th elements
i-=1
print('\nIn reverse order: ')
i=-1 # days[-1] represents last element
while i>=-len(days): # display from -1th to -5th elements
print(days[i])
i-=1
Output:
C:\>python [Link]
In reverse order:
Thursday
Wednesday
Tuesday
Monday
Sunday
In reverse order:
Thursday
Wednesday
Tuesday
Monday
Sunday

Concatenation of Two Lists


We can simply use ‘+’ operator on two lists to join them. For example, ‘x’
and ‘y’ are two
lists. If we write x+y, the list ‘y’ is joined at the end of the list ‘x’.
x = [10,20,30,40,50]
y = [100,110,120]
print(x+y) # concatenate x and y
The concatenated list appears:
[10, 20, 30, 40, 50, 100, 110, 120]

Repetition of Lists
We can repeat the elements of a list ‘n’ number of times using ‘*’ operator.
For example, if
we write x*n, the list ‘x’ will be repeated for n times as:
print(x*2) # repeat the list x for 2 times
Now, the list appears as:
[10, 20, 30, 40, 50, 10, 20, 30, 40, 50]
Membership in Lists
We can check if an element is a member of a list or not by using ‘in’ and ‘not
in’ operator.
If the element is a member of the list, then ‘in’ operator returns True else
False. If the
element is not in the list, then ‘not in’ operator returns True else False. See
the examples
below:
x = [10,20,30,40,50]
a = 20
print(a in x) # check if a is member of x
The preceding statements will give:
True
If you write,
print(a not in x) # check if a is not a member of x
Then, it will give
False
Aliasing and Cloning Lists
Giving a new name to an existing list is called ‘aliasing’. The new name is
called ‘alias
name’. For example, take a list ‘x’ with 5 elements as
x = [10,20,30,40,50]
To provide a new name to this list, we can simply use assignment operator
as:
y=x
In this case, we are having only one list of elements but with two different
names ‘x’ and
‘y’. Here, ‘x’ is the original name and ‘y’ is the alias name for the same list.
Hence, any
modifications done to ‘x’ will also modify ‘y’ and vice versa. Observe the
following
statements where an element x[1] is modified with a new value. This is
shown in
Figure 10.1.
x = [10,20,30,40,50]
y = x # x is aliased as y
print(x) will display [10,20,30,40,50]
print(y) will display [10,20,30,40,50]
x[1] = 99 # modify 1st element in x
print(x) will display [10,99,30,40,50]
print(y) will display [10,99,30,40,50]

Hence, if the programmer wants two independent lists, he should not go for
aliasing. On
the other hand, he should use cloning or copying.
Obtaining exact copy of an existing object (or list) is called ‘cloning’. To clone
a list, we
can take help of the slicing operation as:
y = x[:] # x is cloned as y
When we clone a list like this, a separate copy of all the elements is stored
into ‘y’. The
lists ‘x’ and ‘y’ are independent lists. Hence, any modifications to ‘x’ will not
affect ‘y’ and
vice versa. Consider the following statements:
x = [10,20,30,40,50]
y = x[:] # x is cloned as y
print(x) will display [10,20,30,40,50]
print(y) will display [10,20,30,40,50]
x[1] = 99 # modify 1st element in x
print(x) will display [10,99,30,40,50]
print(y) will display [10,20,30,40,50]
We can observe that in cloning, modifications to a list are confined only to
that list. The
same can be achieved by copying the elements of one list to another using
copy() method.
For example, consider the following statement:
y = [Link]() # x is copied as y
When we copy a list like this, a separate copy of all the elements is stored
into ‘y’. The
lists ‘x’ and ‘y’ are independent. Hence, any modifications to ‘x’ will not
affect ‘y’ and vice
versa. Figure 2 depicts the concept of cloning and copying.
Methods to Process Lists
The function len() is useful to find the number of elements in a list. We can
use this
function as:
n = len(list)
Here, ‘n’ indicates the number of elements of the list. Similar to len()
function, we have
max() function that returns biggest element in the list. Also, the min()
function returns
the smallest element in the list. Other than these function, there are various
other
methods provided by Python to perform various operations on lists. These
methods are
shown in Table 10.1:
We have used these methods in Program 5 to understand how these
methods can be
used on an example list.
Program
Program 5: A Python program to understand list processing methods.
# Python's list methods
num = [10,20,30,40,50]
n = len(num)
print('No. of elements in num: ', n)
[Link](60)
print('num after appending 60: ', num)
[Link](0,5)
print('num after inserting 5 at 0th position: ', num)
num1 = [Link]()
print('Newly created list num1: ', num1)
[Link](num1)
print('num after appending num1: ', num)
n = [Link](50)
print('No. of times 50 found in the list num: ', n)
[Link](50)
print('num after removing 50: ', num)
[Link]()
print('num after removing ending element: ', num)
[Link]()
print('num after sorting: ', num)
[Link]()
print('num after reversing: ', num)
[Link]()
print('num after removing all elements: ', num)
Output:
C:\>python [Link]
No. of elements in num: 5
num after appending 60: [10, 20, 30, 40, 50, 60]
num after inserting 5 at 0th position: [5, 10, 20, 30, 40, 50, 60]
Newly created list num1: [5, 10, 20, 30, 40, 50, 60]
num after appending num1: [5, 10, 20, 30, 40, 50, 60, 5, 10, 20, 30,
40, 50, 60]
No. of times 50 found in the list num: 2
num after removing 50: [5, 10, 20, 30, 40, 60, 5, 10, 20, 30, 40, 50, 60]
num after removing ending element: [5, 10, 20, 30, 40, 60, 5, 10, 20,
30, 40, 50]
num after sorting: [5, 5, 10, 10, 20, 20, 30, 30, 40, 40, 50, 60]
num after reversing: [60, 50, 40, 40, 30, 30, 20, 20, 10, 10, 5, 5]
num after removing all elements: []

Finding Biggest and Smallest Elements in a List


Python provides two functions, max() and min(), which return the biggest
and smallest
elements from a list. For example, if the list is ‘x’, then:
n1 = max(x) # n1 gives the biggest element
n2= min(x) # n2 gives the smallest element
If we do not want to use these functions, but want to find the biggest and
smallest
elements in the list, how is it possible? For this purpose, we should develop
our own
logic.
First of all, we will store the elements in a list. Let’s take the example list as:
x = [20, 10, 5, 20, 15]
The list elements here are represented as x[0], x[1], ... x[4]. That means the
elements in
the list are in general represented as x[i]. We will take the first element as
the biggest and
also as the smallest one as:
big=x[0]
small=x[0]
We will compare ‘big’ and ‘small’ elements with other elements in the list. If
the other
element is > big, then it should be taken as ‘big’. That means,
if x[i]>big: big= x[i]
Similarly, if the other element is < ‘small’, we should take that other element
as ‘small’ as:
if x[i]<small: small = x[i]
Since the comparison starts from 1st element onwards, ‘i’ value will change
from 1 till the
end of the list. This logic is used in Program 6.
Program
Program 6: A Python program to find maximum and minimum elements in a
list of
elements.
# finding biggest and smallest numbers in a list of numbers
x = [] # take an empty list
print('How many elements? ', end='')
n = int(input()) # accept input into n
for i in range(n): # repeat for n times
print('Enter element: ', end='')
[Link](int(input())) # add the element to the list x
print('The list is: ', x) # display the list
big=x[0] # initially 0th element becomes maximum and minimum
small=x[0]
for i in range(1, n): # repeat from 1 to n-1 elements
if x[i]>big: big= x[i] # if any other element is > big, take it as
# big
if x[i]<small: small = x[i] # if any other element is < small,
# take it as small
print('Maximum is: ', big) # display max and min elements
print('Minimum is: ', small)
Output:
C:\>python [Link]
How many elements? 5
Enter element: 20
Enter element: 10
Enter element: 5
Enter element: 20
Enter element: 15
The list is: [20, 10, 5, 20, 15]
Maximum is: 20
Minimum is: 5
Sorting the List Elements
Python provides the sort() method to sort the elements of a list. This method
can be used
as:
[Link]()
This will sort the list ‘x’ into ascending order. If we want to sort the elements
of the list
into descending order, then we can mention ‘reverse=True’ in the sort()
method as:
[Link](reverse=True)
This will sort the list ‘x’ into descending order.
Suppose, we want to sort a list without using sort() method, we have to
develop our own
logic like bubble sort technique. In this technique, all the ‘n’ elements from 0
to n are
taken and the first element of the list x[j] is compared with the immediate
element x[j+1].
If x[j] is bigger than x[j+1], then they are swapped (or interchanged) since in
ascending
order we expect the smaller elements to be in the first place. When two
elements are
interchanged, the number of elements to be sorted becomes lesser by 1.
When there are
no more swaps found, the ‘flag’ will become ‘False’ and we can abort sorting.
Suppose, we
give the following elements for sorting:
Original list: 1, 5, 4, 3, 2
Compare 1 with other elements. 1 is smallest hence no swaps. We get:
1, 5, 4, 3, 2
Compare 5 with other elements. Swap 5 with 4, 5 with 3, 5 with 2. We
get: 1, 4, 3, 2, 5
Compare 4 with other elements. Swap 4 with 3, 4 with 2. We get: 1, 3,
2, 4, 5
Compare 3 with other elements. Swap 3 with 2. We get: 1, 2, 3, 4, 5

Swapping means interchanging the values of two variables. If ‘a’ and ‘b’ are
variables, we
are supposed to store ‘a’ value into ‘b’ and vice versa. Swapping the values
of ‘a’ and ‘b’
can be done using a temporary variable ‘t’ as:
Store a value into t. i.e. t= a
Store b value into a. i.e. a = b
Store t value into b. i.e. b= t
These steps are shown in Figure10.3 by taking ‘a’ value 1 and ‘b’ value 2
initially. After
swapping is done, ‘a’ value will be 2 and ‘b’ value will be 1.

Program 7: A Python program to sort the list elements using bubble sort
technique.
# sorting a list using bubble sort technique
# create an empty list to store integers
x = []
# store elements into the list x
print('How many elements? ', end='')
n = int(input()) # accept input into n
for i in range(n): # repeat for n times
print('Enter element: ', end='')
[Link](int(input())) # add the element to the list x
print('Original list: ', x)
# bubble sort
flag = False # when swapping is done, flag becomes True
for i in range(n-1): # i is from 0 to n-1
for j in range(n-1-i): # j is from 0 to one element lesser than i
if x[j] > x[j+1]: # if 1st element is bigger than the 2nd one
t = x[j] # swap j and j+1 elements
x[j] = x[j+1]
x[j+1] = t
flag = True # swapping done, hence flag is True
if flag==False: # no swapping means list is in sorted order
break # come out of inner for loop
else:
flag = False # assign initial value to flag
print('Sorted list: ', x)
Output:
C:\>python [Link]
How many elements? 5
Enter element: 1
Enter element: 5
Enter element: 4
Enter element: 3
Enter element: 2
Original list: [1, 5, 4, 3, 2]
Sorted list: [1, 2, 3, 4, 5]
Number of Occurrences of an Element in the List
Python provides count() method that returns the number of times a
particular element is
repeated in the list. For example,
n = [Link](y)
will return the number of times ‘y’ is found in the list ‘x’. This method returns
0 if the
element is not found in the list.
It is possible to develop our own logic for the count() method. Let’s take ‘y’
as the element
to be found in the list ‘x’. We will use a counter ‘c’ that counts how many
times the
element ‘y’ is found in the list ‘x’. Initially ‘c’ value will be 0. When ‘y’ is
found in the list,
‘c’ will increment by 1. We need a for loop that iterates over all the elements
of the list as:
for i in x:
This for loop stores each element from the list ‘x’ into ‘i’. So, if ‘y == i’ we
found the
element and hence we should increment i value by 1. This is shown by the
following code
snippet:
c=0
for i in x:
if(y==i): c+=1
print('{} is found {} times.'.format(y, c))
The above code represents our own logic to find the number of occurrences
of ‘y’ in the
list ‘x’. The same is shown in Program 8.
Program
Program 8: A Python program to know how many times an element
occurred in the list.
# counting how many times an element occurred in the list
x = [] # take an empty list
n = int(input('How many elements? ')) # accept input into n
for i in range(n): # repeat for n times
print('Enter element: ', end='')
[Link](int(input())) # add the element to the list x
print('The list is: ', x) # display the list
y = int(input('Enter element to count: '))
c=0
for i in x:
if(y==i): c+=1
print('{} is found {} times.'.format(y, c))
Output:
C:\>python [Link]
How many elements? 5
Enter element: 40
Enter element: 20
Enter element: 30
Enter element: 40
Enter element: 50
The list is: [40, 20, 30, 40, 50]
Enter element to count: 40
40 is found 2 times.
Finding Common Elements in Two Lists
Sometimes, it is useful to know which elements are repeated in two lists. For
example,
there is a scholarship for which a group of students enrolled in a college.
There is another
scholarship for which another group of students got enrolled. Now, we want
to know the
names of the students who enrolled for both the scholarships so that we can
restrict
them to take only one scholarship. That means, we are supposed to find out
the common
students (or elements) in both the lists.
Let’s take the two groups of students as two lists. First of all, we should
convert the lists
into sets, using set() function, as: set(list). Then we should find the common
elements in
the two sets using intersection() method as:
[Link](set2)
This method returns a set that contains common or repeated elements in the
two sets.
This gives us the names of the students who are found in both the sets. This
logic is
presented in Program 9.
Program
Program 9: A Python program to find common elements in two lists.
# finding common elements in two lists
# take two lists
scholar1 = ['Vinay', 'Krishna', 'Saraswathi', 'Govind']
scholar2 = ['Rosy', 'Govind', 'Tanushri', 'Vinay', 'Vishal']
# convert them into sets
s1 = set(scholar1)
s2 = set(scholar2)

# find intersection of two sets


s3 = [Link](s2)
# convert the resultant set into a list
common = list(s3)
# display the list
print(common)
Output:
C:\>python [Link]
['Vinay', 'Govind']
Storing Different Types of Data in a List
The beauty of lists is that they can store different types of elements. For
example, we can
store an integer, a string a float type number etc. in the same string. It is
also possible to
retrieve the elements from the list. This has advantage for lists over arrays.
We can create
list ‘emp’ as an empty list as:
emp = []
Then we can store employee data like id number, name and salary details
into the ‘emp’
list using the append() method. After that, it is possible to retrieve employee
details
depending on id number of the employee. This is shown in Program 10.
Program
Program 10: A Python program to create a list with employee data and then
retrieve a
particular employee details.
# retrieving employee details from a list
emp = [] # take an empty list
n = int(input('How many employees? ')) # accept input into n
for i in range(n): # repeat for n times
print('Enter id: ', end='')
[Link](int(input()))
print('Enter name: ', end='')
[Link](input())
print('Enter salary: ', end='')
[Link](float(input()))
print('The list is created with employee data.')
id = int(input('Enter employee id: '))
# display employee details upon taking id.
for i in range(len(emp)):
if id==emp[i]:
print('Id= {:d}, Name= {:s}, Salary= {:.2f}'.format(emp[i],
emp[i+1], emp[i+2]))
break

Output:
C:\>python [Link]
How many employees? 4
Enter id: 10
Enter name: Vijaya lakshmi
Enter salary: 7000.50
Enter id: 11
Enter name: Gouri shankar
Enter salary: 9500.50
Enter id: 12
Enter name: Anil kumar
Enter salary: 8000
Enter id: 13
Enter name: Hema chandra
Enter salary: 8500.75
The list is created with employee data.
Enter employee id: 12
Id= 12, Name= Anil kumar, Salary= 8000.00
Nested Lists
A list within another list is called a nested list. We know that a list contains
several
elements. When we take a list as an element in another list, then that list is
called a
nested list. For example, we have two lists ‘a’ and ‘b’ as:
a = [80, 90]
b = [10, 20, 30, a]
Observe that the list ‘a’ is inserted as an element in the list ‘b’ and hence ‘a’
is called a
nested list. Let’s display the elements of ‘b’, by writing the following
statement:
print(b)
The elements of b appears:
[10, 20, 30, [80, 90]]
The last element [80, 90] represents a nested list. So, ‘b’ has 4 elements and
they are:
b[0] = 10
b[1] = 20
b[2] = 30
b[3] = [80, 90]
So, b[3] represents the nested list and if we want to display its elements
separately, we
can use a for loop as:
for x in b[3]:
print(x)
Program
Program 11: A Python program to create a nested list and display its
elements.
# To create a list with another list as element
list = [10, 20, 30, [80, 90]]
print('Total list= ', list) # display entire list
print('First element= ', list[0]) # display first element
print('Last element is nested list= ', list[3]) # display nested list
for x in list[3]: # display all elements in nested list
print(x)
Output:
C:\>python [Link]
Total list= [10, 20, 30, [80, 90]]
First element= 10
Last element is nested list= [80, 90]
80
90
In Program 11, we used a for loop to display the nested list. We can refer to
the individual
elements of the nested list, as:
list[3][0] # represents 80
list[3][1] # represents 90
One of the main uses of nested lists is that they can be used to represent
matrices. A
matrix represents a group of elements arranged in several rows and
columns. If a matrix
contains ‘m’ rows and ‘n’ columns, then it is called m x n matrix. In Python,
matrices are
created as 2D arrays or using matrix object in numpy. We can also create a
matrix using
nested lists.
Nested Lists as Matrices
Suppose we want to create a matrix with 3 rows and 3 columns, we should
create a list
with 3 other lists as:
mat = [[1,2,3], [4,5,6], [7,8,9]]
Here, ‘mat’ is a list that contains 3 lists which are rows of the ‘mat’ list. Each
row
contains again 3 elements as:
[[1,2,3], # first row
[4,5,6], # second row
[7,8,9]] # third row
If we use a for loop to retrieve the elements from ‘mat’, it will retrieve row by
row, as:
for r in mat:
print(r) # display row by row
But we want to retrieve columns (or elements) in each row; hence we need
another for
loop inside the previous loop, as:
for r in mat:
for c in r: # display columns in each row
print(c, end=' ')
print()
Another way to display the elements of a matrix is using indexing. For
example, mat[i][j]
represents the ith row and jth column element. Suppose the matrix has ‘m’
rows and ‘n’
columns, we can retrieve the elements as:
for i in range(len(mat)): # i values change from 0 to m-1
for j in range(len(mat[i])): # j values change from 0 to n-1
print()
Program 12 shows various ways of displaying the elements of matrix. Please
pay
attention to this program.
Program
Program 12: A Python program to retrieve elements from a matrix and
display them.
# displaying nested list as a matrix
# take a nested list
mat = [[1,2,3], [4,5,6], [7,8,9]]
print('Display the list as it is: ')
print(mat)
print('Display row by row: ')
for r in mat:
print(r)
print('Display each column in row 0: ')
for c in mat[0]:
print('%d ' %c, end='')
print()
print('Display each column in row 1: ')
for c in mat[1]:
print('%d ' %c, end='')
print()
print('Display each column in row 2: ')
for c in mat[2]:
print('%d ' %c, end='')
print()
print('Display all elements using for: ')
for r in mat:
for c in r: # display columns in each row
print(c, end=' ')
print()
print('Display all elements using for: ')
for i in range(len(mat)):
for j in range(len(mat[i])):
print('%d ' %mat[i][j], end='')
print()
Output:
C:\>python [Link]
Display the list as it is:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Display row by row:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
Display each column in row 0:
123
Display each column in row 1:
456
Display each column in row 2:
789
Display all elements using for:
123
456
789
Display all elements using for:
123
456
789
We can create two matrices using nested lists and find their sum matrix. This
is shown in
Program 13. In this program, m1 and m2 represent matrix of 3 rows and 4
columns
each. To add them we need a simple logic where the elements in
corresponding positions
in the two matrices are added, as:
m3[i][j]= m1[i][j]+m2[i][j]
Program
Program 13: A Python program to add two matrices and display the sum
matrix using
lists.
# nested lists - matrix addition
# take matrix one with 3 rows and 4 cols
m1 = [ [1, 2, 3, 0],
[4, 5, 6, 0],
[7, 8, 9, 0] ]
# take matrix two with 3 rows and 4 cols
m2 = [ [1, 2, 3, 4],
[1, 0, 1, 0],
[2, -1, -2, 1] ]
# take matrix three with 3 rows and 4 cols and initialize with all 0s
m3= [ 4*[0] for i in range(3) ] # repeat four 0s for 3 times
# add the corresponding elements of m1 and m2 and store into m3
for i in range(3):
for j in range(4):
m3[i][j]= m1[i][j]+m2[i][j]
# display the third matrix using for loop
for i in range(3):
for j in range(4):
print('%d ' %m3[i][j], end='')
print()
Output:
C:\>python [Link]
2464
5570
9771

List Comprehensions
List comprehensions represent creation of new lists from an iterable object
(like a list, set,
tuple, dictionary or range) that satisfy a given condition. List comprehensions
contain
very compact code usually a single statement that performs the task.
Example 1: We want to create a list with squares of integers from 1 to 10.
We can write
code as:
squares = [] # create empty list
for x in range(1, 11): # repeat x values from 1 to 10
[Link](x**2) # add squares of x to the list
The preceding code will create ‘squares’ list with the elements as shown
below:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
The previous code can be rewritten in a compact way as:
squares = [x**2 for x in range(1, 11)]
This is called list comprehension. From this, we can understand that a list
comprehension consists of square braces containing an expression (i.e.
x**2). After the
expression, a for loop and then zero or more if statements can be written.
Consider the
following syntax:
[ expression for item1 in iterable1 if statement1
for item2 in iterable2 if statement2
for item3 in iterable3 if statement3 ... ]
Here, ‘iterable’ represents a list, set, tuple, dictionary or range object. The
result of a list
comprehension is a new list that contains elements as a result of executing
the
expression according to the for loops and if statements. So, we can store the
result of a
list comprehension in a new list.
Example 2: Suppose, we want to get squares of integers from 1 to 10 and
take only the
even numbers from the result, we can write a list comprehension as:
even_squares = [x**2 for x in range(1, 11) if x % 2==0]
If we display the list ‘even_squares’, it will display the following list:
[4, 16, 36, 64, 100]
The same list comprehension can be written without using the if statement
as:
even_squares = [x**2 for x in range(2, 11, 2)]
Example 3: If we have two lists ‘x’ and ‘y’ and we want to add each element
of ‘x’ with
each element of ‘y’, we can write for loops as:
x = [10, 20, 30]
y = [1, 2, 3, 4]
lst=[]
for i in x:
for j in y:
[Link](i+j)
Here, the resultant list ‘lst’ contains the following elements:
[11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34]
The same result can be achieved by using list comprehension as:
lst = [i+j for i in x for j in y]
or we can directly mention the lists in the list comprehension as:
lst = [i+j for i in [10, 20, 30] for j in [1,2,3,4]]
The previous list comprehension can be written using strings as:
lst = [ i+j for i in 'ABC' for j in 'DE']
In this case, the output will be:
['AD', 'AE', 'BD', 'BE', 'CD', 'CE']
Example 4: Let’s take a list ‘words’ that contains a group of words or strings
as:
words = ['Apple', 'Grapes', 'Banana', 'Orange']
We want to retrieve only the first letter of each word from the above list and
store those
first letters into another list ‘lst’. We can write code as:
lst = []
for w in words:
[Link](w[0])
Now, lst contains the following letters:
['A', 'G', 'B', 'O']
This task can be achieved through list comprehension as:
lst = [w[0] for w in words]
Example 5: Let’s take two lists ‘num1’ and ‘num2’ with some numbers as:
num1 = [1,2,3,4,5]
num2 = [10,11,1,2]
We want to create another list ‘num3’ with numbers present in ‘num1’ but
not in ‘num2’.
We can develop the logic like this:
num3=[]
for i in num1:
if i not in num2:
[Link](i)
If we display ‘num3’ we can see the following elements:
[3, 4, 5]
The same can be achieved using list comprehension as:
num3 = [i for i in num1 if i not in num2]
Example 6: Like list comprehensions, it is also possible to create set
comprehensions
and dictionary comprehensions in the same manner as discussed in the
previous
examples. Let’s see how to create a dictionary comprehension. A dictionary
contains key
and value pairs separated by colons. A dictionary uses curly braced {} to
embed the
elements. We take a dictionary with hall ticket number and student name as:
dict = {1001: 'Pratap', 1002: 'Mohan', 1003: 'Ankita'}
Here, 1001 is hall ticket number of the student and ‘Pratap’ is the name of
the student.
The hall ticket number is called key and the name is called its value. We can
create
dictionary comprehension to convert keys as values and vice versa as:
dict1 = {value: key for key,value in [Link]()}
Here, [Link]() represents all items in the dictionary in the form of key
value pairs. In
the above expression, we are writing value: key which is the reverse order
for writing key
and value pairs. If we display ‘dict1’, it will show:
{'Pratap': 1001, 'Ankita': 1003, 'Mohan': 1002}

Tuples
A tuple is a Python sequence which stores a group of elements or items.
Tuples are
similar to lists but the main difference is tuples are immutable whereas lists
are mutable.
Since tuples are immutable, once we create a tuple we cannot modify its
elements. Hence
we cannot perform operations like append(), extend(), insert(), remove(),
pop() and clear()
on tuples. Tuples are generally used to store data which should not be
modified and
retrieve that data on demand.
Creating Tuples
We can create a tuple by writing elements separated by commas inside
parentheses ().
The elements can be of same datatype or different types. For example, to
create an empty
tuple, we can simply write empty parenthesis, as:
tup1 = () # empty touple
If we want to create a tuple with only one element, we can mention that
element in
parentheses and after that a comma is needed, as:
tup2 = (10,) # tuple with one element. Observe comma after the element.
Here is a tuple with different types of elements:
tup3 = (10, 20, -30.1, 40.5, 'Hyderabad', 'New Delhi')
We can create a tuple with only one of type of elements also, like the
following:
tup4 = (10, 20, 30) # tuple with integers
If we do not mention any brackets and write the elements separating them
by commas,
then they are taken by default as a tuple. See the following example:
tup5 = 1, 2, 3, 4 #no braces
The point to remember is that if do not use any brackets, it will become a
tuple and not a
list or any other datatype.
It is also possible to create a tuple from a list. This is done by converting a
list into a
tuple using the tuple() function. Consider the following example:
list = [1, 2, 3] # take a list
tpl = tuple(list) # convert list into tuple
print(tpl) # display tuple
The tuple is shown below:
(1, 2, 3)
Another way to create a tuple is by using range() function that returns a
sequence. To
create a tuple ‘tpl’ that contains numbers from 4 to 8 in steps of 2, we can
use the range()
function along with tuple() function, as:
tpl= tuple(range(4, 9, 2)) # numbers from 4 to 8 in steps of 2
print(tpl)
The preceding statements will give:
(4, 6, 8)
Accessing the Tuple Elements
Accessing the elements from a tuple can be done using indexing or slicing.
This is same
as that of a list. For example, let’s take a tuple by the name ‘tup’ as:
tup = (50,60,70,80,90,100)
Indexing represents the position number of the element in the tuple. Now,
tup[0]
represents the 0th element, tup[1] represents the 1st element and so on.
print(tup[0])
The preceding will give:
50
Now, if you write:
print(tup [5])
Then the following element appears:
100
Similarly, negative indexing is also possible. For example, tup[-1] indicates
the last
element and tup[-2] indicates the second element from the end and so on.
Consider the
following statement:
print(tup[-1])
The preceding statement will give:
100
If you write,
print(tup[-6])

Then the following output appears:


50
Slicing represents extracting a piece or part of the tuple. Slicing is done in
the format:
[start: stop: stepsize]. Here, ‘start’ represents the position of the starting
element and
‘stop’ represents the position of the ending element and the ‘stepsize’
indicates the
incrementation. If the tuple contains ‘n’ elements, the default values will be
0 for ‘start’
and n-1 for ‘stop’ and 1 for ‘stepsize’. For example, to extract all the
elements from the
tuple, we can write:
print(tup[:])
The elements of tuple appear:
(50, 60, 70, 80, 90, 100)
For example, to extract the elements from 1st to 4th, we can write:
print(tup[1:4])
The elements of tuple appear:
(60, 70, 80)
Similarly, to extract every other element, i.e. alternate elements, we can
write:
print(tup[::2]) # here, start and stop assume default values.
The following output appears:
(50, 70, 90)
Negative values can be given in the slicing. If the ‘step size’ is negative, the
elements are
extracted in reverse order as:
print(tup[::-2])
The elements appear in the reverse order:
(100, 80, 60)
When the step size is not negative, the elements are extracted from left to
right. In the
following example, starting position -4 indicates the 4th element from the
last. Ending
position -1 indicates the last element. Hence, the elements from 4th to one
element before
the ending element (left to right) will be extracted.
print(tup[-4:-1]) # here, step size is 1
The following elements appear:
(70, 80, 90)
In most of the cases, the extracted elements from the tuple should be stored
in separate
variables for further use. In the following example, we are extracting the first
two
elements from the ‘student’ tuple and storing them into two variables.
student = (10, 'Vinay kumar', 50,60,65,61,70)
rno, name = student[0:2]

Now, the variable ‘rno’ represents 10 and ‘name’ represents Vinaykumar.


Consider the
following statement:
print(rno)
The preceding statement will give:
10
Now, if you write:
print(name)
The preceding statement will provide the name of the student:
Vinay kumar
If we want to retrieve the marks of the student from ‘student’ tuple, we can
do it as:
marks = student[2:7] # store the elements from 2nd to 6th into ‘marks’
# tuple
for i in marks:
print(i)
The preceding statements will give the following output:
50
60
65
61
70

Basic Operations on Tuples


The 5 basic operations: finding length, concatenation, repetition,
membership and
iteration operations can be performed on any sequence may be it is a string,
list, tuple or
a dictionary.
To find length of a tuple, we can use len() function. This returns the number
of elements
in the tuple.
Consider the following example:
student = (10, 'Vinaykumar', 50,60,65,61,70)
len(student)
The preceding statement will give the following output:
7
We can concatenate or join two tuples and store the result in a new tuple. For
example,
the student paid a fees of Rs. 25,000.00 every year for 4 terms, then we can
create a ‘fees’
tuple as:
fees = (25000.00,)*4 # repeat the tuple elements for 4 times.
print(fees)
The preceding statement will give the following output:
(25000.0, 25000.0, 25000.0, 25000.0)

Now, we can concatenate the ‘student’ and ‘fees’ tuples together to form a
new tuple
‘student1’ as:
student1 = student+fees
print(student1)
The preceding statement will give:
(10, 'Vinay kumar', 50, 60, 65, 61, 70, 25000.0, 25000.0, 25000.0,
25000.0)
Searching whether an element is a member of the tuple or not can be done
using ‘in’ and
‘not in’ operators. The ‘in’ operator returns True if the element is a member.
The ‘not in’
operator returns True if the element is not a member. Consider the following
statements:
name='Vinay kumar' # to know if this is member of student1 or not
name in student1
The preceding statements will give:
True
Suppose if you write:
name not in student1
Then the following output will appear:
False
The repetition operator repeats the tuple elements. For example, we take a
tuple ‘tpl’ and
repeat its elements for 4 times as:
tpl = (10, 11, 12)
tpl1 = tpl*3 # repeat for 3 times and store in tpl1
print(tpl1)
The preceding statements will give the following output:
(10, 11, 12, 10, 11, 12, 10, 11, 12)
Functions to Process Tuples
There are a few functions provided in Python to perform some important
operations on
tuples. These functions are mentioned in Table 10.2. Any function is called
directly with
its name. In this table, count() and index() are not functions. They are
methods. Hence,
they are called in the format: [Link]().
Table 10.2: The functions available to process tuples
We will write a program to accept elements of a tuple from the keyboard and
find their
sum and average. In this program, we are using the following statement to
accept
elements from the keyboard directly in the format of a tuple (i.e. inside
parentheses).
num = eval(input("Enter elements in (): "))
The eval() function is useful to evaluate whether the typed elements are a
list or a tuple
depending upon the format of brackets given while typing the elements. If
we type the
elements inside the square braces as: [1,2,3,4,5] then they are considered as
elements of
a list. If we enter the elements inside parentheses as: (1,2,3,4,5), then they
are
considered as elements of a tuple. Even if do not type any brackets, then by
default they
are taken as elements of tuple.
Program
Program 14: A Python program to accept elements in the form of a tuple
and display
their sum and average.
# program to find sum and average of elements in a tuple
num = eval(input("Enter elements in (): "))
sum=0
n=len(num) # n is no. of elements in the tuple
for i in range(n): # repeat i from 0 to n-1
sum+=num[i] # add each element to sum
print('Sum of numbers: ', sum) # display sum
print('Average of numbers: ', sum/n) #display average
Output:
C:\>python [Link]
Enter elements in (): (1,2,3,4,5,6)
Sum of numbers: 21
Average of numbers: 3.5
When the above program asks the user for entering the elements, the user
can type the
elements inside the parentheses as: (1,2,3,4,5,6) or without any parentheses
as:
1,2,3,4,5,6. When a group of elements are entered with commas (, ), then
they are by
default taken as a tuple in Python.
In Program 15, first we enter the elements separated by commas. These
elements are
stored into a string ‘str’ as:
str = input('Enter elements separated by commas: ').split(',')

Then each element of this string is converted into integer and stored into a
list ‘lst’ as:
lst = [int(num) for num in str]
This list can be converted into a tuple using tuple() function as:
tup = tuple(lst)
Later, index() method is used to find the first occurrence of the element ‘ele’
as:
pos = [Link](ele) # returns first occurrence of element
If the ‘ele’ is not found in the tuple, then there will be an error by the name
‘ValueError’
which should be handled using try and except blocks which is shown in the
program.
Program
Program 15: A Python program to find the first occurrence of an element in
a tuple.
# inserting elements from keyboard into the tuple and finding element
# position
# accept elements from keyboard as strings separated by commas
str = input('Enter elements separated by commas: ').split(',')
lst = [int(num) for num in str] # convert strings into integers and
# store into a list
tup = tuple(lst) # convert list into tuple
print('The tuple is: ', tup) # display the tuple
ele = int(input('Enter an element to search: '))
try:
pos = [Link](ele) # returns first occurrence of element
print('Element position no: ', pos+1)
exceptValueError: # if element not found, ValueError will rise
print('Element not found in tuple')
Output:
C:\>python [Link]
Enter elements separated by commas: 10,20,30,20,40
The tuple is: (10, 20, 30, 20, 40)
Enter an element to search: 20
Element position no: 2
Nested Tuples
A tuple inserted inside another tuple is called nested tuple. For example,
tup = (50,60,70,80,90, (200, 201)) # tuple with 6 elements
Observe the last parentheses in the tuple ‘tup’, i.e. (200, 201). These
parentheses
represent that it is a tuple with 2 elements inserted into the tuple ‘tup’. The
tuple (200,
201) is called nested tuple as it is inside another tuple.
The nested tuple with the elements (200, 201) is treated as an element
along with
other elements in the tuple ‘tup’. To retrieve the nested tuple, we can access
it as an ordinary element as tup[5] as its index is 5. Now, consider the
following
statement:
print('Nested tuple= ', tup[6])
The preceding statement will give the following output:
Nested tuple= (200, 201)
Every nested tuple can represent a specific data record. For example, to
store 4
employees data in ‘emp’ tuple, we can write:
emp = ((10, "Vijay", 9000.90), (20, "Nihaar", 5500.75), (30,
"Vanaja",8900.00), (40, "Kapoor", 5000.50))
Here, ‘emp’ is the tuple name. It contains 4 nested tuples each of which
represents the
data of an employee. Every employee’s identification number, name and
salary are stored
as a nested tuples.
Sorting Nested Tuples
To sort a tuple, we can use sorted() function. This function sorts by default
into
ascending order. For example,
print(sorted(emp))
will sort the tuple ‘emp’ in ascending order of the 0th element in the nested
tuples, i.e.
identificaton number. If we want to sort the tuple based on employee name,
which is the
1st element in the nested tuples, we can use a lambda expression as:
print(sorted(emp, key=lambda x: x[1])) # sort on name
Here, key indicates the key for the sorted() function that tells on which
element sorting
should be done. The lambda function: lambda x: x[1] indicates that x[1]
should be taken
as the key that is nothing but 1st element. If we want to sort the tuple based
on salary, we
can use the lambda function as: lambda x: x[2]. Consider Program 16.
Program
Program 16: A Python program to sort a tuple with nested tuples.
# sorting a tuple that contains tuples as elements
# take employee tuple with id number, name and salary
emp = ((10, "Vijay", 9000.90), (20, "Nihaar", 5500.75), (30,
"Vanaja",8900.00), (40, "Kapoor", 5000.50))
print(sorted(emp)) # sorts by default on id
print(sorted(emp, reverse=True)) # reverses on id
print(sorted(emp, key=lambda x: x[1])) # sort on name
print(sorted(emp, key=lambda x: x[2])) # sort on salary

Output:
C:\>python [Link]
[(10, 'Vijay', 9000.9), (20, 'Nihaar', 5500.75), (30, 'Vanaja',
8900.0), (40, 'Kapoor', 5000.5)]
[(40, 'Kapoor', 5000.5), (30, 'Vanaja', 8900.0), (20, 'Nihaar',
5500.75), (10, 'Vijay', 9000.9)]
[(40, 'Kapoor', 5000.5), (20, 'Nihaar', 5500.75), (30, 'Vanaja',
8900.0), (10, 'Vijay', 9000.9)]
[(40, 'Kapoor', 5000.5), (20, 'Nihaar', 5500.75), (30, 'Vanaja',
8900.0), (10, 'Vijay', 9000.9)]
Inserting Elements in a Tuple
Since tuples are immutable, we cannot modify the elements of the tuple
once it is
created. Now, let’s see how to insert a new element into an existing tuple.
Let’stake ‘x’ as
an existing tuple. Since ‘x’ cannot be modified, we have to create a new
tuple ‘y’ with the
newly inserted element. The following logic can be used:
1. First of all, copy the elements of ‘x’ from 0th position to pos-2 position into
‘y’ as:
y = x[0:pos-1]
2. Concatenate the new element to the new tuple ‘y’.
y = y+new
3. Concatenate the remaining elements (from pos-1 till end) of x to the new
tuple ‘y’. The
total tuple can be stored again with the old name ‘x’.
x = y+x[pos-1:]
This logic is depicted in Figure 10.4 and shown in Program 17:
specified position.
# inserting a new element into a tuple
names = ('Visnu', 'Anupama', 'Lakshmi', 'Bheeshma')
print(names)
# accept new name and position number
lst= [input('Enter a new name: ')]
new = tuple(lst)
pos = int(input('Enter position no: '))
# copy from 0th to pos-2 into another tuple names1
names1 = names[0:pos-1]
# concatenate new element at pos-1
names1 = names1+new
# concatenate the remaining elements of names from pos-1 till end
names = names1+names[pos-1:]
print(names)
Output:
C:\>python [Link]
(‘Vishnu’, ‘Anupama’, ‘Lakshmi’, ‘Bheeshma’)
Enter a new name: Ganesh
Enter position no: 2
(‘Vishnu’,’Ganesh’, ‘Anupama’, ‘Lakshmi’, ‘Bheeshma’)
This program works well with strings. Of course the same program can be
used with integers also, if we change the input statement that accepts an
integer number
as:
lst= [int(input('Enter a new integer: '))]

Modifying Elements of a Tuple


It is not possible to modify or update an element of a tuple since tuples are
immutable. If
we want to modify the element of a tuple, we have to create a new tuple
with a new value
in the position of the modified element. The logic used in the previous
section holds good
with a slight difference in the last step. Let’s take ‘x’ is the existing tuple and
‘y’ is the
new tuple.
1. First of all, copy the elements of ‘x’ from 0th position to pos-2 position into
‘y’ as:
y = x[0:pos-1]

2. Concatenate the new element to the new tuple ‘y’. Thus the new element
is stored in
the position of the element being modified.
y = y+new
3. Now concatenate the remaining elements from ‘x’ by eliminating the
element which is
at ‘pos-1’. It means we should concatenate elements from ‘pos’ till the end.
The total
tuple can be assigned again to the old name ‘x’.
x = y+x[pos:]
This logic is depicted in Figure 10.5 and shown in Program 18:

Program 18: A Python program to modify or replace an existing element of


a tuple with a
new element.
# modifying an existing element of a tuple
num = (10, 20, 30, 40, 50)
print(num)
# accept new element and position number
lst= [int(input('Enter a new element: '))]
new = tuple(lst)
pos = int(input('Enter position no: '))
# copy from 0th to pos-2 into another tuple num1
num1 = num[0:pos-1]
# concatenate new element at pos-1
num1 = num1+new
# concatenate the remaining elements of num from pos till end
num = num1+num[pos:]
print(num)
Output:
C:\>python [Link]
(10, 20, 30, 40, 50)
Enter a new element: 88
Enter position no: 3
(10, 20, 88, 40, 50)

Deleting Elements from a Tuple


The simplest way to delete an element from a particular position in the tuple
is to copy all
the elements into a new tuple except the element which is to be deleted.
Let’s assume
that the user enters the position of the element to be deleted as ‘pos’. The
corresponding
position will be ‘pos-1’ in the tuple as the elements start from 0th position.
Now the logic
will be:
1. First of all, copy the elements of ‘x’ from 0th position to pos-2 position into
‘y’ as:
y = x[0:pos-1]
2. Now concatenate the remaining elements from ‘x’ by eliminating the
element which is
at ‘pos-1’. It means we should concatenate elements from ‘pos’ till the end.
The total
tuple can be assigned again to the old name ‘x’.
x = y+x[pos:]
Program
Program 19: A program to delete an element from a particular position in
the tuple.
# deleting an element of a tuple
num = (10, 20, 30, 40, 50)
print(num)
# accept position number of the element to delete
pos = int(input('Enter position no: '))
# copy from 0th to pos-2 into another tuple num1
num1 = num[0:pos-1]
# concatenate the remaining elements of num from pos till end
num = num1+num[pos:]
print(num)

Output:
C:\>python [Link]
(10, 20, 30, 40, 50)
Enter position no: 3
(10, 20, 40, 50)
DICTIONARIES

A dictionary represents a group of elements arranged in the form of key-


value pairs.
In the dictionary, the first element is considered as ‘key’ and the immediate
next
element is taken as its ‘value’. The key and its value are separated by a
colon (:).
All the key-value pairs in a dictionary are inserted in curly braces { }.
Let’s take a dictionary by the name ‘dict’ that contains employee details:
dict = {'Name': 'Chandra', 'Id': 200, 'Salary': 9080.50}
Here, the name of the dictionary is ‘dict’. The first element in the dictionary
is a string
‘Name’. So, this is called ‘key’. The second element is ‘Chandra’ which is
taken as its
‘value’.
Observe that the key and its value are separated by a colon. Similarly, the
next
element is ‘Id’ which becomes ‘key’ and the next element ‘200’ becomes its
value. Finally,
‘Salary’ becomes key and ‘9080.50’ becomes its value. So, we have 3 pairs
of keys and
values in this dictionary.

When the ‘key’ is provided, we can get back its ‘value’. This is how we search
for the
values in a dictionary. For example, ‘Name’ is the key. To get its value, i.e.
‘Chandra’, we
should mention the key as an index to the dictionary, as: dict[‘Name’]. This
will return the
value ‘Chandra’. Similarly, dict[‘Id’] returns its value, i.e. 200. See Program 1.

Program 1: A Python program to create a dictionary with employee details


and retrieve
the values upon giving the keys.
# creating dictionary with key- value pairs
"""
Create a dictionary with employee details.
Here 'Name' is key and 'Chandra' is its value.
'Id' is key and 200 is its value.
'Salary' is key and 9080.50 is its value.
"""
dict = {'Name': 'Chandra', 'Id': 200, 'Salary': 9080.50}
# access value by giving key
print('Name of employee= ', dict['Name'])
print('Id number= ', dict['Id'])
print('Salary= ', dict['Salary'])
Output:
C:\>python [Link]
Name of employee= Chandra
Id number= 200
Salary= 9080.5

Operations on Dictionaries
To access the elements of a dictionary, we should not use indexing or slicing.
For
example, dict[0] or dict[1:3] etc. expressions will give error. To access the
value associated
with a key, we can mention the key name inside the square braces, as:
dict[‘Name’]. This
will return the value associated with ‘Name’. This is nothing but ‘Chandra’.
If we want to know how many key-value pairs are there in a dictionary, we
can use the
len() function, as shown in the following statements:
dict = {'Name': 'Chandra', 'Id': 200, 'Salary': 9080.50}
n = len(dict)
print('No. of key-value pairs = ', n)
The above code will display: No. of key-value pairs = 3. Please remember
each key-value
pair is counted as one element.
We can modify the existing value of a key by assigning a new value, as
shown in the
following statement:
dict['Salary'] = 10500.00
Here, the ‘Salary’ value is modified as ‘10500.00’. The previous value of
‘Salary’, i.e.
9080.50 is replaced by the new value, i.e. 10500.00.
We can also insert a new key-value pair into an existing dictionary. This is
done by
mentioning the key and assigning a value to it, as shown in the following
statement:
dict['Dept'] = 'Finance'

Here, we are giving a new key ‘Dept’ and its value ‘Finance’. This pair is
stored into the
dictionary ‘dict’. Now, if we display the dictionary using print(dict), it will
display:
{'Name': 'Chandra', 'Dept': 'Finance', 'Id': 200, 'Salary': 10500.0}
Observe the new pair ‘Dept’: ’Finance’ is added to the dictionary. Also,
observe that this
pair is not added at the end of existing pairs. It may be added at any place in
the
dictionary.
Suppose, we want to delete a key-value pair from the dictionary, we can use
del
statement as:
del dict['Id']
This will delete the key ‘Id’ and its corresponding value from the dictionary.
Now, the
dictionary looks like this:
{'Name': 'Chandra', 'Dept': 'Finance', 'Salary': 10500.0}
To test whether a ‘key’ is available in a dictionary or not, we can use ‘in’ and
‘not in’
operators. These operators return either True or False. Consider the following
statement:
'Dept' in dict # check if ‘Dept is a key in dict
The preceding statement will give:
True
Now, consider the following statement:
'Gender' in dict # check if ‘Gender’ is a key in dict
The preceding statement will give:
False
Now, if you write:
'Gender' not in dict # check if ‘Gender’ is not a key in dict
Then the following output appears:
True
We can use any datatypes for values. For example, a value can be a number,
string, list,
tuple or another dictionary. But keys should obey the following rules:
 Keys should be unique. It means, duplicate keys are not allowed. If we
enter same
key again, the old key will be overwritten and only the new key will be
available.
Consider the following example:
emp = {'Nag':10, 'Vishnu':20, 'Nag':30} # Key ‘Nag’ entered twice
print(emp)
The output appears as:
{'Nag': 30, 'Vishnu': 20} # first ‘Nag’ is replaced by new key and its
# value
Keys should be immutable type. For example, we can use a number, string or
tuples
as keys since they are immutable. We cannot use lists or dictionaries as keys.
If they
are used as keys, we will get ‘TypeError’. Consider the following example:
emp = {['Nag']:10, 'Vishnu':20, 'Raj':30} # [‘Nag’] is a list element
– so error
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
emp = {['Nag']:10, 'Vishnu':20, 'Raj':30}
TypeError: unhashable type: 'list'
Dictionary Methods
Various methods are provided to process the elements of a dictionary. These
methods
generally retrieve or manipulate the contents of a dictionary. They are
summarized in
Table 11.1:

In Program 2, we are going to retrieve keys from a dictionary using the keys()
method.
The keys() method returns dict_keys object that contains only keys. We will
also retrieve
values from the dictionary using values() method. This method returns all
values in the
form of dict_values object. Similarly, the items() method can be used to
retrieve all key value pairs into dict_items object.

Program 2: A Python program to retrieve keys, values and key-value pairs


from a
dictionary.
# dictionary methods
# create a dictionary with employee details.
dict = {'Name': 'Chandra', 'Id': 200, 'Salary': 9080.50}
# print entire dictionary
print(dict)
# display only keys
print('Keys in dict= ', [Link]())
# display only values
print('Values in dict= ', [Link]())
# display both key and value pairs as tuples
print('Items in dict= ', [Link]())
Output:
C:\>python [Link]
{'Name': 'Chandra', 'Id': 200, 'Salary': 9080.5}
Keys in dict= dict_keys(['Name', 'Id', 'Salary'])
Values in dict= dict_values(['Chandra', 200, 9080.5])
Items in dict= dict_items([('Name', 'Chandra'), ('Id', 200),
('Salary', 9080.5)])
In Program 3, we are going to create a dictionary by entering the elements
from the
keyboard. When we enter the elements from the keyboard inside curly
braces, then they
are treated as key – value pairs of a dictionary by eval() function. Once the
elements are
entered, we want to find sum of the values using sum() function on the
values of the
dictionary.

Program 3: A Python program to create a dictionary and find the sum of


values.
# program to find sum of values in a dictionary
# enter the dictionary entries from keyboard
dict = eval(input("Enter elements in { }: "))
# find the sum of values
s = sum([Link]())
print('Sum of values in the dictionary: ', s) # display sum
Output:
C:\>python [Link]
Enter elements in { }: {'A':10, 'B':20, 'C':35, 'Anil': 50}
Sum of values in the dictionary: 115
In Program 4, first we create an empty dictionary ‘x’. We enter the key into
‘k’ and value
into ‘v’ and then using the update() method, we will store these key-value
pairs into the
dictionary ‘x’, as shown in the following statement:
[Link]({k:v})

Here, the update() method stores the ‘k’ and ‘v’ pair into the dictionary ‘x’.
Program
Program 4: A Python program to create a dictionary from keyboard and
display the
elements.
# creating a dictionary from the keyboard
x = {} # take an empty dictionary
print('How many elements? ', end='')
n = int(input()) # n indicates no. of key-value pairs
for i in range(n): # repeat for n times
print('Enter key: ', end='')
k = input() # key is string
print('Enter its value: ', end='')
v = int(input()) # value is integer
[Link]({k:v}) # store the key-value pair in dictionary x
# display the dictionary
print('The dictionary is: ', x)

Output:
C:\>python [Link]
How many elements? 3
Enter key: Raju
Enter its value: 10
Enter key: Laxmi
Enter its value: 22
Enter key: Salman
Enter its value: 33
The dictionary is: {'Laxmi': 22, 'Raju': 10, 'Salman': 33}

Please observe the output of Program 4. The key-value pairs which are
entered by us
from the keyboard are not displayed in the same order. Dictionaries will not
maintain
orderliness of pairs.
In Program 5, we are creating a dictionary with cricket players’ names and
scores. That
means, player name becomes key and the score becomes its value. Once the
dictionary ‘x’
is created, we can display the players’ names by displaying the keys as:
for pname in [Link](): # keys() will give players names
print(pname)
To find the score of a player, we can use get() method, as:
runs = [Link](name, -1)
In the get() method, we should provide the key, i.e. player name. If the key is
found in the
dictionary, this method returns his ‘runs’. If the player is not found in the
dictionary,
then it returns -1.

Program 5: A Python program to create a dictionary with cricket players


names and
scores in a match. Also we are retrieving runs by entering the player’s name.
# creating a dictionary with cricket players names and scores
x = {} # take an empty dictionary
print('How many players? ', end='')
n = int(input()) # n indicates no. of key-value pairs
for i in range(n): # repeat for n times
print('Enter player name: ', end='')
k = input() # key is string
print('Enter runs: ', end='')
v = int(input()) # value is integer
[Link]({k:v}) # store the key-value pair in dictionary x
# display only players names
print('\nPlayers in this match: ')
for pname in [Link](): # keys() will give only keys
print(pname)
# accept a player name from keyboard
print('Enter player name: ', end='')
name = input()
# find the runs done by the player
runs = [Link](name, -1)
if(runs == -1):
print('Player not found')
else:
print('{} made runs {}.'.format(name, runs))
Output:
C:\>python [Link]
How many players? 3
Enter player name: Sachin
Enter runs: 77
Enter player name: Kohli
Enter runs: 40
Enter player name: Sehwag
Enter runs: 89
Players in this match:
Kohli
Sachin
Sehwag
Enter player name: Kohli
Kohli made runs 40.
Using for Loop with Dictionaries
For loop is very convenient to retrieve the elements of a dictionary. Let’s take
a simple
dictionary that contains color code and its name as:
colors = {'r': "Red", 'g': "Green", 'b': "Blue", 'w': "White"}

Here, ‘r’, ‘g’, ‘w’ represent keys and “Red”, “Green”, “White” indicate values.
Suppose, we
want to retrieve only keys from ‘colors’ dictionary, we can use a for loop as:
for k in colors:
print (k)
In the above loop, ‘k’ stores each element of the colors dictionary. Here, ‘k’
assumes only
keys and hence this loop displays only keys. Suppose, we want to retrieve
values, then
we can obtain them by passing the key to colors dictionary, as: colors[k]. The
following for
loop retrieves all the values from the colors dictionary:
for k in colors:
print (colors[k])
Since values are associated with keys, we can retrieve them only when we
mention the
keys. Suppose, we want to retrieve both the keys and values, we can use the
items()
method in for loop as:
for k, v in [Link]():
print('Key= {} Value= {}'. format(k, v))
In the preceding code, the [Link]() method returns an object by the
name
‘dict_items’ that contains key and value pairs. Each of these pairs is stored
into ‘k’, ‘v’
and then displayed. Consider Program 6.
Program
Program 6: A Python program to show the usage of for loop to retrieve
elements of
dictionaries.
# Using for loop with dictionaries
# take a dictionary
colors = {'r': "Red", 'g': "Green", 'b': "Blue", 'w': "White"}
# display only keys
for k in colors:
print (k)
# pass keys to dictionary and display the values
for k in colors:
print (colors[k])
# items() method returns key and value pair into k, v
for k, v in [Link]():
print('Key= {} Value= {}'. format(k, v))
Output:
C:\>python [Link]
b
w
r
g
Blue
White
Red
Green
Key= b Value= Blue
Key= r Value= Red
Key= g Value= Green
We will write another program to count the number of times each letter has
occurred in a
string. For example, “Book” is the string and we are supposed to find the
number of
occurrences of each letter in this string. It means the letter ‘B’ has occurred
for 1 time,
the letter ‘o’ occurred for 2 times and the letter ‘k’ occurred for 1 time. In
this program,
we use get() method very effectively. Please recollect that the get() method
is used on a
dictionary to retrieve the value by giving the key. If the key is not found in
the dictionary,
then it returns some default value. The format of the get() method is:
[Link](x, 0)
This statement says that if the key ‘x’ is found in the dictionary ‘dict’, then
return its
‘value’ from the dictionary, else return 0. Now, consider the following code:
dict = {} # initially dict is empty
str = "Book" # str indicates “Book”
for x in str: # x indicates each letter of “Book”
dict[x] = [Link](x, 0) + 1
In the preceding code, the last statement is very important.
dict[x] = [Link](x, 0) + 1
Observe the right hand side expression with get() method. It says if ‘x’ (this
is the letter of
the string) is found in the dictionary ‘dict’, then return its value, else return 0.
But we
added ‘1’ to the value returned by get() method and hence, if ‘x’ is not
found, it returns 1.
If ‘x’ is found then it returns the value of ‘x’ plus 1.
Observe the left side expression, i.e. dict[x]. This represents ‘x’ is stored as
key in the
dictionary. Whatever the value returned by the right side expression will be
stored into
that dictionary as value for the key ‘x’. That means:
dict[x] = value returned by get() + 1
Let’s take the first letter of the string, i.e. ‘B’. Since the dictionary is initially
empty, there
are no elements in it and hence ‘B’ is not found in the dictionary. So,
[Link](x, 0) + 1
returns 1. At the left side, we are having dict[x]. It represents dict[‘B’]. Here,
‘B’ is taken
as key. So, the statement becomes:
dict[‘B’] = 1
It means ‘B’ is stored as key and 1 is stored as its value into the dictionary
‘dict’. So the
dictionary contains a pair of elements as: {‘B’: 1}. In the next step, ‘o’ is the
letter for
which the get() method searches in the dictionary. It is not found in the ‘dict’
and hence 1
is returned. So,
dict[‘o’] = 1
This will store the new key – value pair, i.e. ‘o’ and 1 into the ‘dict’ and hence
the
dictionary contains:
{‘B’: 1, ‘o’: 1}.
In the next repetition of for loop, we get ‘o’ into ‘x’. Since it is already
available in the
‘dict’, its value 1 is returned by get() method for which 1 will be added. So,
we get:
dict[‘o’] = 2
It means, the old value of ‘o’ is now updated to 2 in the dictionary and ‘dict’
contains the
elements: {‘B’: 1, ‘o’: 2}. In this way, ‘dict’ stores each letter as key and its
number of
occurrences as value. Table 11.2 summarizes the steps for the string “Book”.
Each letter
of this string is represented by ‘x’.

Program 7: A Python program to find the number of occurrences of each


letter in a string
using dictionary.
# Finding how many times each letter is repeated in a string.
# take a string with some letters
str = "Book"
# take an empty dictionary
dict = {}
# store into dict each letter as key and its
# number of occurrences as value
for x in str:
dict[x] = [Link](x, 0) + 1
# display key and value pairs of dict
for k, v in [Link]():
print('Key = {}\t Its occurrences= {}'.format(k, v))
Output:
C:\>python [Link]
Key = k Its occurrences= 1
Key = o Its occurrences= 2
Key = B Its occurrences= 1
The output of the above program may not show the letter occurrences in an
orderly
manner. This is because the dictionary does not store the elements in the
same order as
they were entered.

Sorting the Elements of a Dictionary using Lambdas


A lambda is a function that does not have a name. Lambda functions are
written using a
single statement and hence look like expressions. Lambda functions are
written without
using ‘def’ keyword. They are useful to perform some calculations or
processing easily.
For example,
f = lambda x, y: x+y
The above expression is a lambda function with 2 arguments, x and y. After
colon (:), we
wrote the body, i.e. x+y. this is the value returned by the lambda function. At
the time of
calling this function, we are supposed to pass 2 values for x and y as: f(10,
15). This will
return 25 as result. For understanding lambdas, kindly refer to the chapter on
‘Functions’.
Let’s take an example dictionary as:
colors = {10: "Red", 35: "Green", 15: "Blue", 25: "White"}
Here, color code and its name are given as key-value pairs. Suppose we want
to sort this
dictionary into ascending order of keys, i.e. on color codes, we can use
sorted() function
in the following format:
sorted(elements, key = color code)
Here, elements of the dictionary can be accessed using the [Link]()
method. A key
can be prescribed using a lambda function as:
key = lambda t: t[0]
Here, ‘t’ is the argument for the lambda function and t[0] is the value
returned by the
function. Since we are supposed to sort the dictionary, we should pass the
entire
dictionary to lambda function. So, ‘t’ represents the dictionary that is passed
to the
function and t[0] represents the 0th element in the dictionary, i.e. color code.
So, the
sorted() function can be written as:
sorted([Link](), key = lambda t: t[0])
This will sort all the elements of the dictionary by taking color code
(indicated by t[0]) as
the key. If we want to sort the dictionary based on color name, then we can
write:
sorted([Link](), key = lambda t: t[1])
This will sort the elements of the dictionary by taking color name (indicated
by t[1]) as the
key. Consider Program 8 to understand how to sort the elements of a
dictionary.

Program 8: A Python program to sort the elements of a dictionary based on


a key or
value.
# Sorting a dictionary by key or value
# take a dictionary
colors = {10: "Red", 35: "Green", 15: "Blue", 25: "White"}
# sort the dictionary by keys, i.e. 0th element
c1 = sorted([Link](), key = lambda t: t[0])
print(c1)
# sort the dictionary by values, i.e. 1st element
c2 = sorted([Link](), key = lambda t: t[1])
print(c2)
Output:
C:\>python [Link]
[(10, 'Red'), (15, 'Blue'), (25, 'White'), (35, 'Green')]
[(15, 'Blue'), (35, 'Green'), (10, 'Red'), (25, 'White')]

Converting Lists into Dictionary


When we have two lists, it is possible to convert them into a dictionary. For
example, we
have two lists containing names of countries and names of their capital
cities.
countries = ["USA", "India", "Germany", "France"]
cities = ['Washington', 'New Delhi', 'Berlin', 'Paris']
We want to create a dictionary out of these two lists by taking the elements
of ‘countries’
list as keys and of ‘cities’ list as values. The dictionary should look something
like this:
d = {"USA" : 'Washington', "India" : 'New Delhi' , "Germany" :
'Berlin', "France" : 'Paris'}
There are two steps involved to convert the lists into a dictionary. The first
step is to
create a ‘zip’ class object by passing the two lists to zip() function as:
z = zip(countries, cities)
The zip() function is useful to convert the sequences into a zip class object.
There may be
1 or more sequences that can be passed to zip() function. Of course, we
passed only 2
lists to zip() function in the above statement. The resultant zip object is ‘z’.
The second step is to convert the zip object into a dictionary by using dict()
function.
d = dict(z)
Here, the 0th element of z is taken as ‘key’ and 1st element is converted into
its ‘value’.
Similarly, 2nd element becomes ‘key’ and 3rd one becomes its ‘value’, etc.
They are stored
into the dictionary ‘d’. If we display ‘d’, we can see the following dictionary:
{'India': 'New Delhi', 'USA': 'Washington', 'Germany': 'Berlin',
'France': 'Paris'}
Program
Program 9: A Python program to convert the elements of two lists into key-
value pairs of
a dictionary.
# converting lists into a dictionary
# take two separate lists with elements
countries = ["USA", "India", "Germany", "France"]
cities = ['Washington', 'New Delhi', 'Berlin', 'Paris']

z = zip(countries, cities)
d = dict(z)
# display key - value pairs from dictionary d
print('{:15s} -- {:15s}'.format('COUNTRY', 'CAPITAL'))
for k in d:
print('{:15s} -- {:15s}'.format(k, d[k]))
Output:
C:\>python [Link]
COUNTRY -- CAPITAL
India -- New Delhi
USA -- Washington
Germany -- Berlin
France – Paris

Converting Strings into Dictionary


When a string is given with key and value pairs separated by some delimiter
(or
separator) like a comma ( , ) we can convert the string into a dictionary and
use it as
dictionary. Let’s take an example string:
str = "Vijay=23,Ganesh=20,Lakshmi=19,Nikhil=22"
This string ‘str’ contains names and their ages. Each pair is separated by a
comma ( , ).
Also, each name and age are separated by equals ( = ) symbol. To convert
such a string
into a dictionary, we have to follow 3 steps. First, we should split the string
into pieces
where a comma is found using split() method and then brake the string at
equals ( = )
symbol. This can be done using a for loop as:
for x in [Link](','):
y= [Link]('=')
Each piece of the string is available in ‘y’. The second step is to store these
pieces into a
list ‘lst’ using append() method as:
[Link](y)
The third step is to convert the list into a dictionary ‘d’ using dict() function
as:
d = dict(lst)
Now, this dictionary ‘d’ contains the elements as:
{'Vijay': '23’, 'Ganesh': '20', 'Lakshmi': '19', 'Nikhil': '22'}
Please observe that this dictionary contains all elements as strings only. See
first pair:
‘Vijay’: ‘23’. Here, ‘Vijay’ is string and his age ‘23’ is also stored as string. If
we want we
can convert this ‘23’ into an integer using int() function. Then we can store
the name and
age into another dictionary ‘d1’ as:
for k, v in [Link]():
d1[k] = int(v) # store k and int(v) as key-value pair into d1.
Here, k represents the key and int(v) represents the converted value being
stored into d1.
This logic is shown in Program 10.

Program 10: A Python program to convert a string into key-value pairs and
store them
into a dictionary. # converting a string into a dictionary
# take a string
str = "Vijay=23,Ganesh=20,Lakshmi=19,Nikhil=22"
# brake the string at ',' and then at '='
# store the pieces into a list lst
lst=[]
for x in [Link](','):
y= [Link]('=')
[Link](y)
# convert the list into dictionary 'd'
# but this 'd' will have both name and age as strings
d = dict(lst)
# create a new dictionary 'd1' with name as string
# and age as integer
d1={}
for k, v in [Link]():
d1[k] = int(v)
# display the final dictionary
print(d1)
Output:
C:\>python [Link]
{'Ganesh': 20, 'Vijay': 23, 'Lakshmi': 19, 'Nikhil': 22}

Passing Dictionaries to Functions


We can pass a dictionary to a function by passing the name of the dictionary.
Let’s define
a function that accepts a dictionary as a parameter.
def fun(dictionary):
for i, j in [Link]():
print(i, '--', j)

d = {'a':'Apple', 'b': 'Book', 'c': 'Cook'}


# call the function and pass the dictionary
fun(d)
Output:
C:\>python [Link]
b -- Book
a -- Apple
c -- Cook
Ordered Dictionaries
We already discussed that the elements of a dictionary are not ordered. It
means the
elements are not stored into the same order as they were entered into the
dictionary.
Sometimes this becomes a problem. For example, take an employee
database in a
company which stores employees details depending on their seniority, i.e.
senior most
employee’s data may be in the beginning of the database. If the employees’
details are
stored in a dictionary, this database will not show the employees details in
the same
order. When the employees’ details are changed, the seniority is disturbed
and the data of
the employee who joined the company first may not appear in the beginning
of the
dictionary. In such a case, the solution is to use ordered dictionaries.
An ordered dictionary is a dictionary but it will keep the order of the
elements. The
elements are stored and maintained in the same order as they were entered
into the
ordered dictionary. We can create an ordered dictionary using the
OrderedDict() method of
‘collections’ module. So, first we should import this method from collections
module, as:
from collections import OrderedDict
Once this is done, we can create an ordered dictionary with the name ‘d’ as:
d = OrderedDict()
We can store the key and values into ‘d’, as:
d[10] = 'A'
d[11] = 'B'
d[12] = 'C'
d[13] = 'D'
Here, 10 is the key and ‘A’ is its value and so on. This order is not disturbed
as ‘d’ is
ordered dictionary. When we display the key – value pairs from the dictionary
‘d’, we can
see the same order. This is shown in Program 12.
Program
Program 12: A Python program to create a dictionary that does not change
the order of
elements.
# create an ordered dictionary
from collections import OrderedDict
d = OrderedDict() # d is ordered dictionary
d[10] = 'A'
d[11] = 'B'
d[12] = 'C'
d[13] = 'D'
# display the ordered dictionary
for i, j in [Link]():
print(i, j)
Output:
C:\>python [Link]
10 A
11 B
12 C
13 D
INTRODUCTION TO OOPS

Languages in which a programmer uses procedures or functions to perform a


task are called Procedure Oriented Programming [Link] C, Pascal,
Fortran etc.,
While developing software, the main task is divided into several sub tasks
and each sub task is represented as a procedure or function.
The main task is thus composed of several procedures and functions. This
approach is called Procedure oriented approach.

Languages that use classes and objects in their programs and are called
Object Oriented Programming languages. Eg C++, Java and Python
A class is a module which itself contains data and methods (functions) to
achieve the task. The main task is divided into several sub tasks, and these
are represented as classes.
Each class can perform several inter-related tasks for which several methods
are written in a class. This approach is called Object Oriented approach.
Problems in Procedure Oriented Approach
Programmer perceives the entire system as fragments of several tasks.
Whenever he wants to perform a new task, he would be writing a new set of
functions.
Thus there is no reusability of an already existing code.
A new task every time requires developing the code from the scratch.
This wastes programmer’s time and effort.
In Procedure Oriented approach, every task and sub task is represented as a
function
and one function may depend on another function. Hence, an error in the
software needs
examination of all the functions. Thus debugging or removing errors will
become difficult.
Any updations to the software will also be difficult.

Software developed following the Procedure Oriented approach that when


the code size exceeds 10,000 lines and before reaching 100,000 lines,
suddenly at a particular point, the programmers start losing control on the
code. This
means, the programmers could not understand the exact behavior of the
code and could
neither debug it, nor extend it.

In OOPS,programming will have several modules.


Each module represents a ‘class’ and the classes can be reusable and hence
maintenance of code will become easy.
When there is an error, it is possible to debug only on that class where error
occurred without disturbing the other classes.
This approach is suitable not only to develop bigger and complex
applications but also to manage them easily. Moreover, this approach is built
from a single root concept ‘object’, which represents anything that physically
exists in this world.
It means that all human beings are objects. All animals are objects. All
existing things
will become objects. This new approach is called ‘Object Oriented Approach’.

Programming in this approach is called Object Oriented Programming System


(OOPS).
In OOPS, everything is an object. In real life, some objects will have similar
behavior.
For example, all birds have similar behavior like having two wings, two legs,
etc.
Also, all birds have the ability to fly in the sky. Such objects with similar
behavior belong to the same class.
So, a class represents common behavior of a group of objects. Since a class
represents behavior, it does not exist physically. But objects exist physically.
For example, bird is a class; whereas, sparrow, pigeon, crow and peacock are
objects of the bird class.
Similarly, human being is a class and Arjun, Krishna, Sita are objects of the
human being class

Specialty of Python Language


Even though, Python is an object oriented programming language like Java, it
does not
force the programmers to write programs in complete object oriented way.
Unlike Java,
Python has a blend of both the object oriented and procedure oriented
features. Hence,
Python programmers can write programs using procedure oriented approach
(like C) or
object oriented approach (like Java) depending on their requirements. This is
definitely
an advantage for Python programmers!

Features of Object Oriented Programming System (OOPS)


There are five important features related to Object Oriented Programming
System. They
are:
Classes and objects
Encapsulation
Abstraction
Inheritance
Polymorphism
Let’s move further to have clear understanding of each of these features.

This definition specifies that everything in this world is an object. For


example, a table, a
ball, a car, a dog, a person, etc. will come under objects. Then what is not an
object? If
something does not really exist, then it is not an object. For example, our
thoughts,
imagination, plans, ideas etc. are not objects, because they do not physically
exist.

Every object has some behavior. The behavior of an object is represented by


attributes
and actions.
For example, let’s take a person whose name is ‘Raju’. Raju is an object
because he exists physically. He has attributes like name, age, sex, etc.
These attributes
can be represented by variables in our programming. For example, ’name’ is
a string type
variable, ‘age’ is an integer type variable.
Similarly, Raju can perform some actions like talking, walking, eating and
sleeping. We
may not write code for such actions in programming. But, we can consider
calculations
and processing of data as actions. These actions are performed by methods.
We should
understand that a function written inside a class is called a method. So an
object
contains variables and methods.
It is possible that some objects may have similar behavior. Such objects
belong to same
category called a ‘class’. For example, not only Raju, but all the other
persons have
various common attributes and actions. So they are all objects of same class,
‘Person’.
Now observe that the ‘Person’ will not exist physically but only Raju, Ravi,
Sita, etc. exist
physically. This means, a class is a group name and does not exist physically,
but objects
exist physically.
A class is a model or blueprint for creating objects. By following
the class, one can create objects. So we can say, whatever is there in the
class, will be
seen in its objects also.
Object is called ‘instance’ (physical form) of a class.

Creating Classes and Objects in Python


Let’s create a class with the name Person for which Raju and Sita are objects.
A class is
created by using the keyword, class. A class describes the attributes and
actions
performed by its objects. So, we write the attributes (variables) and actions
(functions) in
the class as:
# This is a class
class Person:
# attributes means variables
name = 'Raju'
age = 20
# actions means functions
def talk(cls):
print([Link])
print([Link])

Observe the preceding code. Person class has two variables and one
function. The
function that is written in the class is called method. When we want to use
this class, we
should create an object to the class as:
p1 = Person()
Here, p1 is an object of Person class. Object represents memory to store the
actual data.
The memory needed to create p1 object is provided by PVM. Observe the
function (or
method) in the class:
def talk(cls):
Here, ‘cls’ represents a default parameter that indicates the class. So,
[Link] refers to
class variable ‘Raju’.
We can call the talk() method to display Raju’s details as:
[Link]()

Encapsulation
Encapsulation is a mechanism where the data (variables) and the code
(methods) that act
on the data will bind together. For example, if we take a class, we write the
variables and
methods inside the class.
Thus, class is binding them together. So class is an example for
encapsulation.
The variables and methods of a class are called ‘members’ of the class.
All the members of a class are by default available outside the class. That
means they are public by default.
Public means available to other programs and classes. Python follows
Uniform
Access Principle that says that in OOPS, all the members of the class
whether they are
variables or methods should be accessible in a uniform manner. So, Python
variables and
methods are available outside alike. That means both are public by default.
Usually, in C++ and Java languages, the variables are kept private, that
means they are not available outside the class and the methods are kept
public meaning that they are available to other programs. But in Python,
both the variables and methods are public by default.

Encapsulation isolates the members of a class from the members of another


class. The
reason is when objects are created, each object shares different memory and
hence there
will not be any overwriting of data.
This gives an advantage to the programmer to use same names for the
members of two different classes.
For example, a programmer can declare and use the variables like ‘id’,
‘name’, and ‘address’ in different classes like Employee, Customer, or
Student classes.

Encapsulation in Python
Encapsulation is nothing but writing attributes (variables) and methods
inside a class.
The methods process the data available in the variables. Hence data and
code are
bundled up together in the class. For example, we can write a Student class
with ‘id’ and
‘name as attributes along with the display() method that displays this data.
This Student
class becomes an example for encapsulation.
# a class is an example for encapsulation
class Student:
# to declare and initialize the variables
def __init__(self):
[Link] = 10
[Link] = 'Raju'
# display students details
def display(self):
print([Link])
print([Link])

Observe the first method: def __init__(self). This is called a special function
since its name
is starting and ending with two underscores. If a variable or method name
starts and
ends with two underscores, they are built-in variables or methods which are
defined for a
specific purpose. The programmer should not create any variable or method
like them. It
means we should not create variables or methods with two underscores
before and after
their names.
The purpose of the special method def __init__(self) is to declare and initialize
the
instance variables of a class. Instance variables are the variables whose copy
is available
in the object (or instance). The first parameter for this method is ‘self’ that
represents the
object (or instance) of the present class. So, [Link] refers to the variable in
the object. In
the Student class, we have written another method by the name display()
that displays
the instance variables.

Abstraction
There may be a lot of data, a class contains and the user does not need the
entire data.
The user requires only some part of the available data. In this case, we can
hide the
unnecessary data from the user and expose only that data that is of interest
to the user.
This is called abstraction.
A good example for abstraction is a car. Any car will have some parts like
engine,
radiator, battery, mechanical and electrical equipment etc. The user of the
car (driver)
should know how to drive the car and does not require any knowledge of
these parts. For
example driver is never bothered about how the engine is designed and the
internal parts
of the engine. This is why the car manufacturers hide these parts from the
driver in a
separate panel, generally at the front of the car.

to his requirements and will not get confused with unnecessary data. A bank
clerk
should see the customer details like account number, name and balance
amount in the
account. He should not be entitled to see the sensitive data like the staff
salaries, profit
or loss of the bank, interest amount paid by the bank, loans amount to be
recovered, etc.
Hence, such sensitive data can be abstracted from the clerk’s view. The bank
manager
may, however, require the sensitive data and so it will be provided to the
manager.
Abstraction in Python
In languages like Java, we have keywords like private, protected and public to
implement
various levels of abstraction. These keywords are called access specifiers. In
Python,
such words are not available. Everything written in the class will come under
public.
That means everything written in the class is available outside the class to
other people.
Suppose, we do not want to make a variable available outside the class or to
other
members inside the class, we can write the variable with two double scores
before it as:
__var. This is like a private variable in Python. In the following example, ‘y’ is
a private
variable since it is written as: __y.
class Myclass:
# this is constructor.
def __init__(self):
self.__y = 3 # this is private variable
Now, it is not possible to access the variable from within the class or out of
the class as:
m = Myclass()
print(m.y) # error
The preceding print() statement displays error message as: AttributeError:
‘Myclass’
object has no attribute ‘y’. Even though, we cannot access the private
variable in this
way, it is possible to access it in the format:
instancename.__Classname__var. That
means we are using Classname differently to access the private variable.
This is called
name mangling. In name mangling, we have to use one underscore before
the classname
and two underscores after the classname. Like this, using the names
differently to access
the private variables is called name mangling. For example, to display a
private variable
‘y’ value, we can write:
print(m._Myclass__y) # display private variable y
The same statement can be written inside the method as:
print(self._Myclass__z). When
we use single underscore before a variable as _var, then that variable or
object will not be
imported into other files. The following code represents the public and
private variables
and how to access them.
# understanding public and private variables
class Myclass:
# this is constructor.
def __init__(self):
self.x = 1 # public var
self.__y = 2 # private var
# instance method to access variables
def display(self):
print(self.x) # x is available directly
print(self._Myclass__y) # name mangling required
print('Accessing variables through method:')
m = Myclass()
[Link]()
print('Accessing variables through instance:')
print(m.x) # x is available directly
print(m._Myclass__y) # name mangling required
Output:
C:\>python [Link]
Accessing variables through method:
1
2
Accessing variables through instance:
1
2
We are planning to write Bank class with ‘accno’, ‘name’, ‘balance’ and ‘loan’
as variables.
Since the clerk should not see the loan amount of the customer, we can write
that
variable with two underscores before the variable, as: ‘ __loan’. Then this
variable is not
available directly outside the class or inside the class to other methods.
In the Bank class, the first method is a special method with the name:
__init__(self) is
useful to declare variables and initialize them with some data. In the
program, ‘self’
represents current class object. In this method, we are making loan variable
as private by
writing it as:
self. __loan = 1500000.00;
This variable is not available outside the class. It is not even available to
other methods
in the same class. Hence, it is abstracted completely from the user of the
class. If the
bank clerk calls the display_to_clerk(self) method, he will be able to see
account number,
name and balance amount only. He cannot see loan amount of the customer.
That means
some part of the data is hidden from the clerk. See the example code:
# accessing some part of data
class Bank :
def __init__(self):
[Link] = 10
[Link] = 'Srinu'
[Link] = 5000.00
self. __loan = 1500000.00
def display_to_clerk(self):
print([Link])
print([Link])
print([Link])
In the preceding class, in spite of several data items, the display_to_clerk()
method is able
to access and display only the ‘accno’, ‘name’ and ‘balance’ values. It cannot
access loan
of the customer. This means the loan data is hidden from the view of the
bank clerk. This
is called abstraction. Suppose, we try to display the loan amount in the
display_to_clerk()
method, by writing:
print([Link])
This raises an error saying ‘loan’ is not an attribute of Bank class.
Inheritance
Creating new classes from existing classes, so that the new classes will
acquire all the
features of the existing classes is called Inheritance. A good example for
Inheritance in
nature is parents producing the children and children inheriting the qualities
of the
parents.
Let’s take a class A with some members i.e., variables and methods. If we
feel another
class B wants almost same members, then we can derive or create class B
from A as:
class B(A):
Now, all the features of A are available to B. If an object to B is created, it
contains all the
members of class A and also its own members. Thus, the programmer can
access and
use all the members of both the classes A and B. Thus, class B becomes
more useful.
This is called inheritance. The original class (A) is called the base class or
super class and
the derived class (B) is called the sub class or derived class.
There are three advantages of inheritance. First, we can create more useful
classes
needed by the application (software). Next, the process of creating the new
classes is very
easy, since they are built upon already existing classes. The last, but very
important
advantage is managing the code becomes easy, since the programmer
creates several
classes in a hierarchical manner, and segregates the code into several
modules.
An Example for Inheritance in Python
Here, we take a class A with two variables ‘a’ and ’b’ and a method,
method1(). Since all
these members are needed by another class B, we extend class B from A. We
want some
additional members in B, for example a variable ‘c’ and a method,
method2(). So, these
are written in B. Now remember, class B can use all the members of both A
and B. This
means the variables ‘a’,’b’,’c ‘and also the methods method1() and
method2() are available
to class B. That means all the members of A are inherited by B.
# Creating class B from class A
class A :
a=1
b=2
def method1(cls):
print(cls.a)
print(cls.b)
class B(A):
c=3
def method2(cls):
print(cls.c)
By creating an object to B, we can access all the members of both the
classes A and B.
Polymorphism
The word ‘Polymorphism’ came from two Greek words ‘poly’ meaning ‘many’
and
‘morphos’ meaning ‘forms’. Thus, polymorphism represents the ability to
assume several
different forms. In programming, if an object or method is exhibiting different
behavior in
different contexts, it is called polymorphic nature.
Polymorphism provides flexibility in writing programs in such a way that the
programmer
uses same method call to perform different operations depending on the
requirement.
Example Code for Polymorphism in Python:
When a function can perform different tasks, we can say that it is exhibiting
polymorphism. A simple example is to write a function as:
def add(a, b):
print(a+b)
Since in Python, there the variables are not declared explicitly, we are
passing two
variables ‘a’, and ‘b’ to add() function where they are added. While calling
this function, if
we pass two integers like 5 and 15, then this function displays 15. If we pass
two strings,
then the same function concatenates or joins those strings. That means the
same
function is adding two integers or concatenating two strings. Since the
function is
performing two different tasks, it is said to exhibit polymorphism. Now,
consider the
following example:
# a function that exhibits polymorphism
def add(a, b):
print(a+b)
# call add() and pass two integers
add(5, 10) # displays 15
# call add() and pass two strings
add("Core", "Python") # displays CorePython

The programming languages which follow all the five features of OOPS are
called object oriented programming languages. For example, C++, Java and
Python will come into this
category.

CLASSES AND OBJECTS

know that a class is a model or plan to create objects. This means, we write a
class with the attributes and actions of objects. Attributes are represented by
variables and actions are performed by methods. So, a class contains
variable
and methods. The same variables and methods are also available in the
objects because
they are created from the class. These variables are also called ‘instance
variables’
because they are created inside the instance (i.e. object).
Please remember the difference between a function and a method. A
function written
inside a class is called a method. Generally, a method is called using one of
the following
two ways:
[Link]()
[Link]()
The general format of a class is given as follows:
Class Classname(object):
""" docstring describing the class """
attributes
def __init__(self):
def method1():
def method2():

Creating a Class
After the Classname, ‘object’ is written inside the Classname. This ‘object’
represents the base class name from where all classes in Python are derived.
Even our own classes are also derived from ‘object’ class. Hence, we should
mention ‘object’ in the parentheses. Please note that writing ‘object’ is not
compulsory
since it is implied.
The docstring is a string which is written using triple double quotes or triple
single
quotes that gives the complete description about the class and its usage.
The docstring is
used to create documentation file and hence it is optional. ‘attributes’ are
nothing but
variables that contains data. __init__(self) is a special method to initialize the
variables.
method1() and method2(), etc. are methods that are intended to process
variables.
If we take ‘Student’ class, we can write code in the class that specifies the
attributes
and actions performed by any student. For example, a student has attributes
like name, age, marks, etc. These attributes should be written inside the
Student class as variables. Similarly, a student can perform actions like
talking, writing, reading, etc. These actions should be represented by
methods in
the Student class. So, the class Student contains these attributes and
actions, as
shown here:
class Student: # another way is: class Student(object):
# the below block defines attributes
def __init__(self):
[Link] = ‘Vishnu’
[Link] = 20
[Link] = 900
# the below block defines a method
def talk(self):
print(‘Hi, I am ‘, [Link])
print(‘My age is’, [Link])
print(‘My marks are’, [Link])
Observe that the keyword class is used to declare a class. After this, we
should write
the class name. So, ‘Student’ is our class name. Generally, a class name
should start
with a capital letter, hence ‘S’ is capital in ‘Student’. In the class, we write
attributes
and methods. Since in Python, we cannot declare variables, we have written
the
variables inside a special method, i.e. __init__(). This method is useful to
initialize the
variables. Hence, the name ‘init’. The method name has two underscores
before and
after. This indicates that this method is internally defined and we cannot call
this
method explicitly. Observe the parameter ‘self’ written after the method
name in the
parentheses. ‘self’ is a variable that refers to current class instance. When
we create
an instance for the Student class, a separate memory block is allocated on
the heap
and that memory location is by default stored in ‘self’. The instance contains
the
variables ‘name’, ‘age’, ‘marks’ which are called instance variables. To refer
to
instance variables, we can use the dot operator notation along with self as:
‘[Link]’, ‘[Link]’ and ‘[Link]’.
See the method talk(). This method also takes the ‘self’ variable as
parameter. This
method displays the values of the variables by referring them using ‘self’.

The methods that act on instances (or objects) of a class are called instance
methods.
Instance methods use ‘self’ as the first parameter that refers to the location
of the
instance in the memory. Since instance methods know the location of
instance, they can
act on the instance variables. In the previous code, the two methods
__init__(self) and
talk(self) are called instance methods.
In the Student class, a student is talking to us through talk() method. He is
introducing
himself to us, as shown here:
Hi, I am Vishnu
My age is 20
My marks are 900
This is what the talk() method displays. Writing a class like this is not
sufficient.
It should be used. To use a class, we should create an instance (or object) to
the
class. Instance creation represents allotting memory necessary to store the
actual data
of the variables, i.e., Vishnu, 20 and 900. To create an instance, the following
syntax is
used:
instancename = Classname()
So, to create an instance (or object) to the Student class, we can write as:
s1 = Student()
Here, ‘s1’ is nothing but the instance name. When we create an instance like
this, the
following steps will take place internally:
1. First of all, a block of memory is allocated on heap. How much memory
is to be allocated is decided from the attributes and methods available in the
Student class.
2. After allocating the memory block, the special method by the name
‘__init__(self)’ is
called internally. This method stores the initial data into the variables. Since
this
method is useful to construct the instance, it is called ‘constructor’.
3. Finally, the allocated memory location address of the instance is returned
into ‘s1’
variable. To see this memory location in decimal number format, we can use
id()
function as id(s1).
Now, ‘s1’ refers to the instance of the Student class. Hence any variables or
methods in
the instance can be referenced by ‘s1’ using dot operator as:
[Link] # this refers to data in name variable, i.e. Vishnu
[Link] # this refers to data in age variable, i.e. 20
[Link] # this refers to data in marks variable, i.e. 900
[Link]() # this calls the talk() method

The dot operator takes the instance name at its left and the member of the
instance at
the right hand side. Figure 13.1 shows how ‘s1’ instance of Student class is
created in
memory:
Program 1: A Python program to define Student class and create an object
to it. Also, we

will call the method and display the student’s details.


# instance variables and instance method
class Student:
# this is a special method called constructor.
def __init__(self):
[Link] = 'Vishnu'
[Link] = 20
[Link] = 900
# this is an instance method.
def talk(self):
print('Hi, I am', [Link])
print('My age is', [Link])
print('My marks are', [Link])
# create an instance to Student class.
s1 = Student()
# call the method using the instance.
[Link]()
Output:
C:\>python [Link]
Hi, I am Vishnu
My age is 20
My marks are 900

In Program 1, we used the ‘self’ variable to refer to the instance of the same
class. Also,
we used a special method ‘__init__(self)’ that initializes the variables of the
instance. Let’s
have more clarity on these two concepts.

The Self Variable


‘self’ is a default variable that contains the memory address of the instance
of the current
class. So, we can use ‘self’ to refer to all the instance variables and instance
methods.
When an instance to the class is created, the instance name contains the
memory
location of the instance. This memory location is internally passed to ‘self’.
For example,
we create an instance to Student class as:
s1 = Student()
Here, ‘s1’ contains the memory address of the instance. This memory
address is
internally and by default passed to ‘self’ variable. Since ‘self’ knows the
memory address
of the instance, it can refer to all the members of the instance. We use ‘self’
in two ways:
The ‘self’ variable is used as first parameter in the constructor as:
def __init__(self):
In this case, ‘self’ can be used to refer to the instance variables inside the
constructor.
‘self’ can be used as first parameter in the instance methods as:
def talk(self):
Here, talk() is instance method as it acts on the instance variables. If this
method
wants to act on the instance variables, it should know the memory location
of the
instance variables. That memory location is by default available to the talk()
method
through ‘self’.

Constructor
A constructor is a special method that is used to initialize the instance
variables of a
class. In the constructor, we create the instance variables and initialize them
with some
starting values. The first parameter of the constructor will be ‘self’ variable
that contains
the memory address of the instance. For example,
def __init__(self):
[Link] = ‘Vishnu’
[Link] = 900
Here, the constructor has only one parameter, i.e. ‘self’. Using ‘[Link]’
and
‘[Link]’, we can access the instance variables of the class. A constructor
is called at
the time of creating an instance. So, the above constructor will be called
when we create
an instance as:
s1 = Student()
Here, ‘s1’ is the name of the instance. Observe the empty parentheses after
the class
name ‘Student’. These empty parentheses represent that we are not passing
any values to
the constructor. Suppose, we want to pass some values to the constructor,
then we have
to pass them in the parentheses after the class name. Let’s take another
example. We can
write a constructor with some parameters in addition to ‘self’ as:
def __init__(self, n = ‘’, m=0):
[Link] = n
[Link] = m
Here, the formal arguments are ‘n’ and ‘m’ whose default values are given as
‘’ (None) and
0 (zero). Hence, if we do not pass any values to constructor at the time of
creating an
instance, the default values of these formal arguments are stored into name
and marks
variables. For example,
s1 = Student()
Since we are not passing any values to the instance, None and zero are
stored into name
and marks. Suppose, we create an instance as:
s1 = Student(‘Lakshmi Roy’, 880)
In this case, we are passing two actual arguments: ‘Lakshmi Roy’ and 880 to
the Student
instance. Hence these values are sent to the arguments ‘n’ and ‘m’ and from
there stored
into name and marks variables. We can understand this concept from
Program 2.

Program 2: A Python program to create Student class with a constructor


having more
than one parameter.
# instance vars and instance method - v.20
class Student:
# this is constructor.
def __init__(self, n = '', m=0):
[Link] = n
[Link] = m
# this is an instance method.
def display(self):
print('Hi', [Link])
print('Your marks', [Link])
# constructor is called without any arguments
s = Student()
[Link]()
print('------------------')
# constructor is called with 2 arguments
s1 = Student('Lakshmi Roy', 880)
[Link]()
print('------------------')
Output:
C:\>python [Link]
Hi
Your marks 0
------------------
Hi Lakshmi Roy
Your marks 880
------------------

We should understand that a constructor does not create an instance. The


duty of the
constructor is to initialize or store the beginning values into the instance
variables. A
constructor is called only once at the time of creating an instance. Thus, if 3
instances
are created for a class, the constructor will be called once per each instance,
thus it is
called 3 times.

Types of Variables
The variables which are written inside a class are of 2 types:
 Instance variables
 Class variables or Static variables
Instance variables are the variables whose separate copy is created in every
instance (or
object). For example, if ‘x’ is an instance variable and if we create 3
instances, there will
be 3 copies of ‘x’ in these 3 instances. When we modify the copy of ‘x’ in any
instance, it
will not modify the other two copies. Consider Program 3.
Program
Program 3: A Python program to understand instance variables.
# instance vars example
class Sample:
# this is a constructor.
def __init__(self):
self.x = 10
# this is an instance method.
def modify(self):
self.x+=1
# create 2 instances
s1 = Sample()
s2 = Sample()
print(‘x in s1= ‘, s1.x)
print(‘x in s2= ‘, s2.x)
# modify x in s1
[Link]()
print(‘x in s1= ‘, s1.x)
print(‘x in s2= ‘, s2.x)
Output:
C:\>python [Link]
x in s1= 10
x in s2= 10
x in s1= 11
x in s2= 10

Instance variables are defined and initialized using a constructor with ‘self’
parameter.
Also, to access instance variables, we need instance methods with ‘self’ as
first
parameter. It is possible that the instance methods may have other
parameters in
addition to the ‘self’ parameter. To access the instance variables, we can use
[Link]
as shown in Program 3. It is also possible to access the instance variables
from outside
the class, as: [Link], e.g. s1.x.
Unlike instance variables, class variables are the variables whose single copy
is available
to all the instances of the class. If we modify the copy of class variable in an
instance, it
will modify all the copies in the other instances. For example, if ‘x’ is a class
variable and
if we create 3 instances, the same copy of ‘x’ is passed to these 3 instances.
When we
modify the copy of ‘x’ in any instance using a class method, the modified
copy is sent to
the other two instances. This can be easily grasped from Program 4. Class
variables are
also called static variables.
Program
Program 4: A Python program to understand class variables or static
variables.
# class vars or static vars example
class Sample:
# this is a class var
x = 10
# this is a class method.
@classmethod
def modify(cls):
cls.x+=1
# create 2 instances
s1 = Sample()
s2 = Sample()
print(‘x in s1= ‘, s1.x)
print(‘x in s2= ‘, s2.x)
# modify x in s1
[Link]()
print(‘x in s1= ‘, s1.x)
print(‘x in s2= ‘, s2.x)
Output:
C:\>python [Link]
x in s1= 10
x in s2= 10
x in s1= 11
x in s2= 11
Observe Program 4. The class variable ‘x’ is defined in the class and
initialized with value
10. A method by the name ‘modify’ is used to modify the value of ‘x’. This
method is
called ‘class method’ since it is acting on the class variable. To mark this
method as class
method, we should use built-in decorator statement @classmethod. For
example,
@classmethod # this is a decorator
def modify(cls): # cls must be the first parameter
cls.x+=1 # cls.x refers to class variable x

A class method contains first parameter by default as ‘cls’ with which we can
access the
class variables. For example, to refer to the class variable ‘x’, we can use
‘cls.x’. We can
also write other parameters in the class method in addition to the ‘cls’
parameter. The
point is that the class variables are defined directly in the class. To access
class
variables, we need class methods with ‘cls’ as first parameter. We can access
the class
variables using the class methods as: [Link]. If we want to access the
class variables
from outside the class, we can use: [Link], e.g. Sample.x.
Namespaces
A namespace represents a memory block where names are mapped (or
linked) to objects.
Suppose we write:
n = 10
Here, ‘n’ is the name given to the integer object 10. Please recollect that
numbers,
strings, lists etc. are all considered as objects in Python. The name ‘n’ is
linked to 10 in
the namespace. A class maintains its own namespace, called ‘class
namespace’. In the
class namespace, the names are mapped to class variables. Similarly, every
instance will
have its own name space, called ‘instance namespace’. In the instance
namespace, the
names are mapped to instance variables. In the following code, ‘n’ is a class
variable in
the Student class. So, in the class namespace, the name ‘n’ is mapped or
linked to 10 as
shown Figure 13.2. Since it is a class variable, we can access it in the class
namespace,
using [Link], as: Student.n which gives 10.

# understanding class namespace


class Student:
# this is a class var
n=10
# access class var in the class namespace
print(Student.n) # displays 10
Student.n+=1 # modify it in class namespace
print(Student.n) # displays 11

We know that a single copy of class variable is shared by all the instances.
So, if the class
variable is modified in the class namespace, since same copy of the variable
is modified,
the modified copy is available to all the instances. This is shown in Figure
13.2.
# modified class var is seen in all instances
s1 = Student() # create s1 instance
print(s1.n) # displays 11
s2 = Student() # create s2 instance
print(s2.n) # displays 11
What happens when the class variable is modified in the instance
namespace? Since
every instance will have its own namespace, if the class variable is modified
in one
instance namespace, it will not affect the variables in the other instance
namespaces.
This is shown in Figure 13.3. To access the class variable at the instance
level, we have
to create instance first and then refer to the variable as
[Link].

# understanding instance namespace


class Student:
# this is a class var
n=10
# access class var in the s1 instance namespace
s1 = Student()
print(s1.n) # displays 10
s1.n+=1 # modify it in s1 instance namespace
print(s1.n) # displays 11
As per the above code, we created an instance ‘s1’ and modified the class
variable ‘n’ in
that instance. So, the modified value of ‘n’ can be seen only in that instance.
When we
create other instances like ‘s2’, there will be still the original value of ‘n’
available. See the
code below:
# modified class var is not seen in other instances
s2 = Student() # this is another instance
print(s2.n) # displays 10, not 11

Types of Methods
By this time, we got some knowledge about the methods written in a class.
The purpose
of a method is to process the variables provided in the class or in the
method. We already
know that the variables declared in the class are called class variables (or
static
variables) and the variables declared in the constructor are called instance
variables. We
can classify the methods in the following 3 types:
 Instance methods
(a) Accessor methods
(b) Mutator methods
 Class methods
 Static methods

Instance Methods
Instance methods are the methods which act upon the instance variables of
the class.
Instance methods are bound to instances (or objects) and hence called as:
[Link](). Since instance variables are available in the
instance, instance
methods need to know the memory address of the instance. This is provided
through ‘self’
variable by default as first parameter for the instance method. While calling
the instance
methods, we need not pass any value to the ‘self’ variable.
Program 5 is an extension to our previous Student class. In this program, we
are creating
a Student class with a constructor that defines ‘name’ and ‘marks’ as
instance variables.
An instance method display() will display the values of these variables. We
added another
instance methods by the name calculate() that calculates the grades of the
student
depending on the ‘marks’.

Program 5: A Python program using a student class with instance methods


to process
the data of several students.
# instance methods to process data of the objects
class Student:
# this is a constructor.
def __init__(self, n = ‘’, m=0):
[Link] = n
[Link] = m
# this is an instance method.
def display(self):
print(‘Hi’, [Link])
print(‘Your marks’, [Link])
# to calculate grades based on marks.
def calculate(self):
if([Link]>=600):
print(‘You got first grade’)
elif([Link]>=500):
print(‘You got second grade’)
elif([Link]>=350):
print(‘You got third grade’)
else:
print(‘You are failed’)
# create instances with some data from keyboard
n = int(input(‘How many students? ‘))
i=0
while(i<n):
name = input(‘Enter name: ‘)
marks = int(input(‘Enter marks: ‘))
# create Student class instance and store data
s = Student(name, marks)
[Link]()
[Link]()
i+=1
print(‘---------------------‘)
Output:
C:\>python [Link]
How many students? 3
Enter name: Vishnu Vardhan
Enter marks: 800
Hi Vishnu Vardhan
Your marks 800
You got first grade
---------------------
Enter name: Tilak Prabhu
Enter marks: 360
Hi Tilak Prabhu
Your marks 360
You got third grade
---------------------
Enter name: Gunasheela
Enter marks: 550
Hi Gunasheela
Your marks 550
You got second grade
---------------------
Instance methods are of two types: accessor methods and mutator methods.
Accessor
methods simply access or read data of the variables. They do not modify the
data in the
variables. Accessor methods are generally written in the form of getXXX()
and hence they
are also called getter methods. For example,

def getName(self):
return [Link]
Here, getName() is an accessor method since it is reading and returning the
value of
‘name’ instance variable. It is not modifying the value of the name variable.
On the other
hand, mutator methods are the methods which not only read the data but
also modify
them. They are written in the form of setXXX() and hence they are also called
setter
methods. For example,
def setName(self, name):
[Link] = name
Here, setName() is a mutator method since it is modifying the value of
‘name’ variable by
storing new name. In the method body, ‘[Link]’ represents the instance
variable
‘name’ and the right hand side ‘name’ indicates the parameter that receives
the new value
from outside. In Program 6, we are redeveloping the Student class using
accessor and
mutator methods.

Program 6: A Python program to store data into instances using mutator


methods and to
retrieve data from the instances using accessor methods.

# accessor and mutator methods


class Student:
# mutator method
def setName(self, name):
[Link] = name
# accessor method
def getName(self):
return [Link]
# mutator method
def setMarks(self, marks):
[Link] = marks
# accessor method
def getMarks(self):
return [Link]
# create instances with some data from keyboard
n = int(input(‘How many students? ‘))
i=0
while(i<n):
# create Student class instance
s = Student()
name = input(‘Enter name: ‘)
[Link](name)
marks = int(input(‘Enter marks: ‘))
[Link](marks)
# retrieve data from Student class instance
print(‘Hi’, [Link]())
print(‘Your marks’, [Link]())
i+=1
print(‘-------------------‘)
Output:
C:\>python [Link]
How many students? 2
Enter name: Vinay Krishna
Enter marks: 890
Hi Vinay Krishna

Your marks 890


------------------
Enter name: Vimala Rao
Enter marks: 750
Hi Vimala Rao
Your marks 750
------------------
Since mutator methods define the instance variables and store data, we
need not write
the constructor in the class to initialize the instance variables. This is the
reason we did
not use constructor in Student class in Program 6.

Class Methods
These methods act on class level. Class methods are the methods which act
on the class
variables or static variables. These methods are written using @classmethod
decorator
above them. By default, the first parameter for class methods is ‘cls’ which
refers to the
class itself. For example, ‘[Link]’ is the format to refer to the class variable.
These
methods are generally called using the [Link](). The processing
which is
commonly needed by all the instances of the class is handled by the class
methods. In
Program 7, we are going to develop Bird class. All birds in the Nature have
only 2 wings.
So, we take ‘wings’ as a class variable. Now a copy of this class variable is
available to all
the instances of Bird class. The class method fly() can be called as [Link]().

Program 7: A Python program to use class method to handle the common


feature of all
the instances of Bird class.
# understanding class methods
class Bird:
# this is a class var
wings = 2
# this is a class method
@classmethod
def fly(cls, name):
print(‘{} flies with {} wings’.format(name, [Link]))
# display information for 2 birds
[Link](‘Sparrow’)
[Link](‘Pigeon’)
Output:
C:\>python [Link]
Sparrow flies with 2 wings
Pigeon flies with 2 wings

Static Methods
We need static methods when the processing is at the class level but we
need not involve
the class or instances. Static methods are used when some processing is
related to the
class but does not need the class or its instances to perform any work. For
example,
setting environmental variables, counting the number of instances of the
class or
changing an attribute in another class, etc. are the tasks related to a class.
Such tasks
are handled by static methods. Also, static methods can be used to accept
some values,
process them and return the result. In this case the involvement of neither
the class nor
the objects is needed. Static methods are written with a decorator
@staticmethod above
them. Static methods are called in the form of [Link](). In
Program 8, we are
creating a static method noObjects() that counts the number of objects or
instances
created to Myclass. In Myclass, we have written a constructor that
increments the class
variable ‘n’ every time an instance is created. This incremented value of ‘n’
is displayed by
the noObjects() method.
Program
Program 8: A Python program to create a static method that counts the
number of
instances created for a class.
# understanding static methods
class Myclass:
# this is class var or static var
n=0
# constructor that increments n when an instance is created
def __init__(self):
Myclass.n = Myclass.n+1
# this is a static method to display the no. of instances
@staticmethod
def noObjects():
print(‘No. of instances created: ‘, Myclass.n)
# create 3 instances
obj1 = Myclass()
obj2 = Myclass()
obj3 = Myclass()
[Link]()
Output:
C:\>python [Link]
No. of instances created: 3
In the next program, we accept a number from the keyboard and return the
result of its
square root value. Here, there is no need of class or object and hence we can
write a
static method to perform this task.

Program 9: A Python program to create a static method that calculates the


square root
value of a given number.
# a static method to find square root value
import math
class Sample:
@staticmethod
def calculate(x):
result = [Link](x)
return result
# accept a number from keyboard
num = float(input('Enter a number: '))
# call the static method and pass num
res = [Link](num)
print('The square root of {} is {:.2f}'.format(num, res))
Output:
C:\>python [Link]
Enter a number: 49
The square root of 49.0 is 7.00
In Program 10, we are creating a Bank class. An account in the bank is
characterized by
name of the customer and balance amount in the account. Hence a
constructor is written
that defines ‘name’ and ‘balance’ attributes. If balance is not given, then it is
taken as 0.0.
The deposit() method is useful to handle the deposits and the withdraw()
method is useful
to handle the withdrawals. Bank class can be used by creating an instance
‘b’ to it as:
b = Bank(name)
Since the constructor expects the name of the customer, we have to pass
the name in the
parentheses while creating the instance of the Bank class. In the while loop,
we are
displaying a one line menu as:
print('d -Deposit, w -Withdraw, e -Exit')
When the user choice is ‘e’ or ‘E’, we will terminate the program by calling
the exit()
method of ‘sys’ module.

Program 10: A Python program to create a Bank class where deposits and
withdrawals
can be handled by using instance methods.
# A class to handle deposits and withdrawals in a bank
import sys
class Bank(object):
""" Bank related transactions """
# to initialize name and balance instance vars
def __init__(self, name, balance=0.0):
[Link] = name
[Link] = balance
# to add deposit amount to balance
def deposit(self, amount):
[Link] += amount
return [Link]
# to deduct withdrawal amount from balance
def withdraw(self, amount):
if amount >[Link]:
print('Balance amount is less, so no withdrawal.')
else:
[Link] -= amount
return [Link]
# using the Bank class
# create an account with the given name and balance 0.00
name = input('Enter name: ')
b = Bank(name) # this is instance of Bank class
# repeat continuously till choice is 'e' or 'E'.
while(True):
print('d -Deposit, w -Withdraw, e -Exit')
choice = input('Your choice: ')
if choice == 'e' or choice == 'E':
[Link]()
# amount for deposit or withdraw
amt = float(input('Enter amount: '))
# do the transaction
if choice == 'd' or choice == 'D':
print('Balance after deposit: ', [Link](amt))
elif choice == 'w' or choice == 'W':
print('Balance after withdrawal: ', [Link](amt))
Output:
C:\>python [Link]
Enter name: Madhuri
d -Deposit, w -Withdraw, e -Exit
Your choice: d
Enter amount: 10000
Balance after deposit: 10000.0
d -Deposit, w -Withdraw, e -Exit
Your choice: w
Enter amount: 3500
Balance after withdrawal: 6500.0
d -Deposit, w -Withdraw, e -Exit
Your choice: e

Passing Members of One Class to Another Class


It is possible to pass the members (i.e. attributes and methods) of a class to
another
class. Let’s take an Emp class with a constructor that defines attributes ‘id’,
‘name’, and
‘salary’. This class has an instance method display() to display these values.
If we create
an object (or instance) of Emp class, it contains a copy of all the attributes
and methods.
To pass all these members of Emp class to another class, we should pass
Emp class
instance to the other class. For example, let’s create an instance of Emp
class as:

e = Emp()
Then pass this instance ‘e’ to a method of other class, as:
[Link](e)
Here, Myclass is the other class and mymethod() is a static method that
belongs to
Myclass. In Myclass, the method mymethod() will be declared as a static
method as it
acts neither on the class variables nor instance variables of Myclass. The
purpose of

mymethod() is to change the attribute of Emp class. For example,


mymethod() may
increment the employee salary by 1000 rupees as shown below:
def mymethod(e):
# increment salary in e by 1000
[Link]+=1000; # modify attribute of Emp class
[Link]() # call the method of Emp class
So, the point is this: by passing the instance of a class, we are passing all the
attributes
and methods to another class. In the other class, it is possible to utilize them
as needed.
In our example, Myclass method, i.e. mymethod() is utilizing the salary
attribute and
display() methods of Emp class.
Program
Program 11: A Python program to create Emp class and make all the
members of the
Emp class available to another class, i.e. Myclass.
# this class contains employee details
class Emp:
# this is a constructor.
def __init__(self, id, name, salary):
[Link] = id
[Link] = name
[Link] = salary
# this is an instance method.
def display(self):
print('Id=', [Link])
print('Name=', [Link])
print('Salary= ', [Link])
# this class displays employee details
class Myclass:
# method to receive Emp class instance
# and display employee details
@staticmethod
def mymethod(e):
# increment salary of e by 1000
[Link]+=1000;
[Link]()
# create Emp class instance e
e = Emp(10, 'Raj kumar', 15000.75)
# call static method of Myclass and pass e
[Link](e)

Output:
C:\>python [Link]
Id= 10
Name= Raj kumar
Salary= 16000.75
Let’s understand that static methods are used when the class variables or
instance
variables are not disturbed. We have to use a static method when we want to
pass some
values from outside and perform some calculation in the method. Here, we
are not
touching the class variable or instance variables. Program 12 shows a static
method that
calculates the value of a number raised to a power.

Program 12: A Python program to calculate power value of a number with


the help of a
static method.
# another example for static method
class Myclass:
# method to calculate x to the power of n
@staticmethod
def mymethod(x, n):
result = x**n
print('{} to the power of {} is {}'.format(x, n, result))
# call the static method
[Link](5, 3)
[Link](5, 4)
Output:
C:\>python [Link]
5 to the power of 3 is 125
5 to the power of 4 Is 625

Inner Classes
Writing a class within another class is called creating an inner class or nested
class. For
example, if we write class B inside class A, then B is called inner class or
nested class.
Inner classes are useful when we want to sub group the data of a class. For
example, let’s
take a person’s data like name, age, date of birth etc. Here, name contains a
single value
like ‘Charles’, age contains a single value like ‘30’ but the date of birth does
not contain a
single value. Rather, it contains three values like date, month and year. So,
we need to
take these three values as a sub group. Hence it is better to write date of
birth as a
separate class Dob inside the Person class. This Dob will contain instance
variables dd,
mm and yy which represent the date of birth details of the person.
Generally, the inner class object is created within the outer class. Let’s take
Person class
as outer class and Dob as inner class. Dob class object is created in the
constructor of
the Person class as:

class Person:
def __init__(self):
[Link] = 'Charles'
[Link] = [Link]() # this is Dob object
In the preceding code, ‘db’ represents the inner class object. When the outer
class object
is created, it contains a sub object that is inner class object. Hence, we can
refer outer
class and inner class members as:
p = Person() # create outer class object
[Link]() # call outer class method
print([Link]) # refer to outer class instance variable

x = [Link] # create inner class object


[Link]() # call inner class method
print([Link]) # refer to inner class instance variable
Program
Program 13: A Python program to create Dob class within Person class.
# inner class example
class Person:
def __init__(self):
[Link] = 'Charles'
[Link] = [Link]()
def display(self):
print('Name= ', [Link])
# this is inner class
class Dob:
def __init__(self):
[Link] = 10
[Link] = 5
[Link] = 1988
def display(self):
print('Dob= {}/{}/{}'.format([Link], [Link], [Link]))
# creating Person class object
p = Person()
[Link]()
# create inner class object
x = [Link]
[Link]()
Output:
C:\>python [Link]
Name= Charles
Dob= 10/5/1988
It is not compulsory to create inner class object inside outer class. That
means, we need
not write the following statement in the Person class constructor:
[Link] = [Link]()
If we do not write this, then there is no relation between the outer class
object and inner
class object. Then how to refer to inner class members is the question. In this
case, we
have to create outer class object and then using dot operator, we should
mention the
inner class object as:
x = Person().Dob() # create inner class object
[Link]() # call inner class method
print([Link]) # refer to inner class instance variable
This concept is shown in Program 14.

Program 14: A Python program to create another version of Dob class


within Person class.
# inner class example - v2.0
class Person:
def __init__(self):
[Link] = 'Charles'
def display(self):
print('Name= ', [Link])
# this is inner class
class Dob:
def __init__(self):
[Link] = 10
[Link] = 5
[Link] = 1988
def display(self):
print('Dob= {}/{}/{}'.format([Link], [Link], [Link]))
# creating Person class object
p = Person()
[Link]()
# create Dob class object as sub object to Person class object
x = Person().Dob()
[Link]()
print([Link])
Output:
C:\>python [Link]
Name= Charles
Dob= 10/5/1988
1988

INHERITANCE AND POLYMORPHISM


Program 1: A Python program to create Teacher class and store it into
[Link]
module.
# this is Teacher class. save this code in [Link] file
class Teacher:
def setid(self, id):
[Link] = id
def getid(self):
return [Link]
def setname(self, name):
[Link] = name
def getname(self):
return [Link]
def setaddress(self, address):
[Link] = address
def getaddress(self):
return [Link]

def setsalary(self, salary):


[Link] = salary
def getsalary(self):
return [Link]
When the programmer wants to use this Teacher class that is available in
[Link] file,
he can simply import this class into his program and use it as shown here:
Program
Program 2: A Python program to use the Teacher class.
# save this code as [Link] file
# using Teacher class
from teacher import Teacher
# create instance
t = Teacher()
# store data into the instance
[Link](10)
[Link]('Prakash')
[Link]('HNO-10, Rajouri gardens, Delhi')
[Link](25000.50)
# retrieve data from instance and display
print('id= ', [Link]())
print('name= ', [Link]())
print('address= ', [Link]())
print('salary= ', [Link]())
Output:
C:\>python [Link]
id= 10
name= Prakash
address= HNO-10, Rajouri gardens, Delhi
salary= 25000.5
So, the program is working well. There is no problem. Once the Teacher class
is
completed, the programmer stored [Link] program in a central database
that is
available to all the members of the team. So, Teacher class is made available
through the
module [Link], as shown in Figure 14.1:
Now, another programmer in the same team wants to create a Student class.
He is planning the Student class without considering the Teacher class as
shown in
Program 3.
Program
Program 3: A Python program to create Student class and store it into
[Link]
module.
# this is Student class –v1.0. save it as [Link]
class Student:
def setid(self, id):
[Link] = id
def getid(self):
return [Link]
def setname(self, name):
[Link] = name
def getname(self):
return [Link]
def setaddress(self, address):
[Link] = address
def getaddress(self):
return [Link]
def setmarks(self, marks):
[Link] = marks
def getmarks(self):
return [Link]
Now, the second programmer who created this Student class and saved it as
[Link]
can use it whenever he needs. Using the Student class is shown in Program
4.
Program
Program 4: A Python program to use the Student class which is already
available in
[Link]
# save this code as [Link]
# using Student class
from student import Student
# create instance
s = Student()
# store data into the instance
[Link](100)
[Link]('Rakesh')
[Link]('HNO-22, Ameerpet, Hyderabad')
[Link](970)

# retrieve data from instance and display


print('id= ', [Link]())
print('name= ', [Link]())
print('address= ', [Link]())
print('marks= ', [Link]())
Output:
C:\>python [Link]
id= 100
name= Rakesh
address= HNO-22, Ameerpet, Hyderabad
marks= 970
So far, so nice! If we compare the Teacher class and the Student classes, we
can
understand that 75% of the code is same in both the classes. That means
most of the
code being planned by the second programmer in his Student class is
already available in
the Teacher class. Then why doesn’t he use it for his advantage? Our idea is
this: instead
of creating a new class altogether, he can reuse the code which is already
available. This
is shown in Program 5.
Program
Program 5:A Python program to create Student class by deriving it from the
Teacher
class.
# Student class - [Link] it as [Link]
from teacher import Teacher
class Student(Teacher):
def setmarks(self, marks):
[Link] = marks
def getmarks(self):
return [Link]
The preceding code will be same as the first version of the Student class.
Observe this
code. In the first statement we are importing Teacher class from teacher
module so that
the Teacher class is now available to this program. Then we are creating
Student class
as:
class Student(Teacher):
This means the Student class is derived from Teacher class. Once we write
like this, all
the members of Teacher class are available to the Student class. Hence we
can use them
without rewriting them in the Student class. In addition, the following two
methods are
needed by the Student class but not available in the Teacher class:
def setmarks(self, marks):
def getmarks(self):
Hence, we wrote only these two methods in the Student class. Now, we can
use the
Student class as we did earlier. Creating the instance to the Student class
and calling the
methods as:
# create instance
s = Student()
# store data into the instance
[Link](100)
[Link]('Rakesh')
[Link]('HNO-22, Ameerpet, Hyderabad')
[Link](970)
# retrieve data from instance and display
print('id= ', [Link]())
print('name= ', [Link]())
print('address= ', [Link]())
print('marks= ', [Link]())
In other words, we can say that we have created Student class from the
Teacher class.
This is called inheritance. The original class, i.e. Teacher class is called base
class or
super class and the newly created class, i.e. the Student class is called the
sub class or
derived class. So, how can we define inheritance? Deriving new classes from
the existing
classes such that the new classes inherit all the members of the existing
classes, is called
inheritance. The syntax for inheritance is:
class Subclass(Baseclass):
The next question is why the base class members are automatically available
to sub
class? When an object to Student class is created, it contains a copy of
Teacher class
within it. This means there is a relation between the Teacher class and
Student class
objects. This is the reason Teacher class members are available to Student
class. Note
that we do not create Teacher class object, but still a copy of it is available to
Student
class object. Please see the object diagram of Student class in Figure 14.2.
We can
understand that all the members (i.e., variables and methods) of Teacher
class as well as
Student class are available in the Student class object.
Then, what is the advantage of inheritance? Please look at Student class
version 1 and
Student class version 2. Clearly, second version is smaller and easier to
develop. By
using inheritance, a programmer can develop the classes very easily. Hence
programmer’s productivity is increased. Productivity is a term that refers to
the code
developed by the programmer in a given span of time. If the programmer
used
inheritance, he will be able to develop more code in less time. So, his
productivity is
increased. This will increase the overall productivity of the organization,
which means
more profits for the organization and better growth for the programmer.
In inheritance, we always create only the sub class object. Generally, we do
not create
super class object. The reason is clear. Since all the members of the super
class are
available to sub class, when we create an object, we can access the
members of both the
super and sub classes. But if we create an object to super class, we can
access only the
super class members and not the sub class members.
Constructors in Inheritance
In the previous programs, we have inherited the Student class from the
Teacher class. All
the methods and the variables in those methods of the Teacher class (base
class) are
accessible to the Student class (sub class). Are the constructors of the base
class
accessible to the sub class or not – is the next question we will answer. In
Program 6, we
are taking a super class by the name ‘Father’ and derived a sub class ‘Son’
from it. The
Father class has a constructor where a variable ‘property’ is declared and
initialized with
800000.00. When Son is created from Father, this constructor is by default
available to
Son class. When we call the method of the super class using sub class object,
it will
display the value of the ‘property’ variable.

Program 6: A Python program to access the base class constructor from sub
class.
# base class constructor is available to sub class
class Father:
def __init__(self):
[Link] = 800000.00
def display_property(self):
print('Father\'s property= ', [Link])
class Son(Father):
pass # we do not want to write anything in the sub class
# create sub class instance and display father's property
s = Son()
s.display_property()
Output:
C:\>python [Link]
Father's property= 800000.0
The conclusion is this: like the variables and methods, the constructors in the
super
class are also available to the sub class object by default.
Overriding Super Class Constructors and Methods
When the programmer writes a constructor in the sub class, the super class
constructor
is not available to the sub class. In this case, only the sub class constructor is
accessible
from the sub class object. That means the sub class constructor is replacing
the super
class constructor. This is called constructor overriding. Similarly in the sub
class, if we
write a method with exactly same name as that of super class method, it will
override the
super class method. This is called method overriding. Consider Program 7.
Program
Program 7: A Python program to override super class constructor and
method in sub
class.
# overriding the base class constructor and method in sub class
class Father:
def __init__(self):
[Link] = 800000.00
def display_property(self):
print('Father\'s property= ', [Link])
class Son(Father):
def __init__(self):
[Link] = 200000.00
def display_property(self):
print('Child\'s property= ', [Link])
# create sub class instance and display father's property
s = Son()
s.display_property()
Output:
C:\>python [Link]
Child’s property= 200000.00
In Program 7, in the sub class, we created a constructor and a method with
exactly same
names as those of super class. When we refer to them, only the sub class
constructor
and method are executed. The base class constructor and method are not
available to the
sub class object. That means they are overridden. Overriding should be done
when the
programmer wants to modify the existing behavior of a constructor or
method in his sub
class.
In this case, how to call the super class constructor so that we can access the
father’s
property from the Son class? For this purpose, we should call the constructor
of the
super class from the constructor of the sub class using the super() method.
The super() Method
super() is a built-in method which is useful to call the super class constructor
or methods
from the sub class. Any constructor written in the super class is not available
to the sub
class if the sub class has a constructor. Then how can we initialize the super
class
instance variables and use them in the sub class? This is done by calling the
super class
constructor using the super() method from inside the sub class constructor.
super() is a
built-in method in Python that contains the history of super class methods.
Hence, we
can use super() to refer to super class constructor and methods from a sub
class. So
super() can be used as:
super().__init__() # call super class constructor
super().__init__(arguments)# call super class constructor and pass
# arguments
super().method() # call super class method
When there is a constructor with parameters in the super class, we have to
create another
constructor with parameters in the sub class and call the super class
constructor using
super() from the sub class constructor. In the following example, we are
calling the super
class constructor and passing ‘property’ value to it from the sub class
constructor.
# this is sub class constructor
def __init__(self, property1=0, property=0):
super().__init__(property) # send property value to superclass
# constructor
self.property1= property1 # store property1 value into subclass
# variable
As shown in the preceding code, the sub class constructor has 2 parameters.
They are
‘property1’ and ‘property’. So, when we create an object (or instance) to sub
class, we
should pass two values, as:
s = Son(200000.00, 800000.00)
Now, the first value 200000 is stored into ‘property1’ and the second value
800000.00 is
stored into ‘property’. Afterwards, this ‘property’ value is sent to super class
constructor
in the first statement of the sub class constructor. This is shown in Program
8.
Program
Program 8: A Python program to call the super class constructor in the sub
class using
super().
# accessing base class constructor in sub class
class Father:
def __init__(self, property=0):
[Link] = property
def display_property(self):
print('Father\'s property= ', [Link])
class Son(Father):
def __init__(self, property1=0, property=0):
super().__init__(property)
self.property1= property1
def display_property(self):
print('Total property of child= ', self.property1 + [Link])
# create sub class instance and display father's property
s = Son(200000.00, 800000.00)
s.display_property()
Output:
C:\>python [Link]
Total property of child= 1000000.0
To understand the use of super() in a better way, let’s write another Python
program
where we want to calculate areas of a square and a rectangle. Here, we are
writing a
Square class with one instance variable ‘x’ since to calculate the area of
square, we need
one value. Another class Rectangle is derived from Square. So, the value of
‘x’ is inherited
by Rectangle class from Square class. To calculate area of rectangle we need
two values.
So, we take a constructor with two parameters ‘x’ and ‘y’ in the sub class. In
this
program, we are calling the super class constructor and passing ‘x’ value as:
super().__init__(x)
We are also calling super class area() method as:
super().area()
In this way, super() can be used to refer to the constructors and methods of
super class.

Program 9: A Python program to access base class constructor and method


in the sub
class using super().
# Accessing base class constructor and method in the sub class
class Square:
def __init__(self, x):
self.x = x
def area(self):
print('Area of square= ', self.x*self.x)
class Rectangle(Square):
def __init__(self, x, y):
super().__init__(x)
self.y = y
def area(self):
super().area()
print('Area of rectangle= ', self.x*self.y)
# find areas of square and rectangle
a, b = [float(x) for x in input("Enter two measurements: ").split()]
r = Rectangle(a,b)
[Link]()
Output:
C:\>python [Link]
Enter two measurements: 10 5.5
Area of square= 100.0
Area of rectangle= 55.0

Types of Inheritance
As we have seen so far, the main advantage of inheritance is code
reusability. The
members of the super class are reusable in the sub classes. Let’s remember
that all
classes in Python are built from a single super class called ‘object’. If a
programmer
creates his own classes, by default object class will become super class for
them
internally. This is the reason, sometimes while creating any new class, we
mention the
object class name in parentheses as:
class Myclass(object):
Here, we are indicating that object is the super class for Myclass. Of course,
writing
object class name is not mandatory and hence the preceding code is
equivalent to
writing:
class Myclass:
Now, coming to the types of inheritance, there are mainly 2 types of
inheritance available.
They are:
 Single inheritance
 Multiple inheritance

Single Inheritance
Deriving one or more sub classes from a single base class is called ‘single
inheritance’. In
single inheritance, we always have only one base class, but there can be n
number of sub
classes derived from it. For example, ‘Bank’ is a single base class from where
we derive
‘AndhraBank’ and ‘StateBank’ as sub classes. This is called single
inheritance. Consider
Figure 14.3. It is convention that we should use the arrow head towards the
base class
(i.e. super class) in the inheritance diagrams.

In Program 10, we are deriving two sub classes AndhraBank and StateBank
from the
single base class, i.e. Bank. All the members (i.e. variables and methods) of
Bank class
will be available to the sub classes. In the Bank class, we have some ‘cash’
variable and a
method to display that, as:
class Bank(object):
cash = 100000000
@classmethod
def available_cash(cls):
print([Link])
Here, the class variable ‘cash’ is declared in the class and initialized to 10
crores. The
available_cash() is a class method that is accessing this variable as
‘[Link]’. When we
derive AndhraBank class from Bank class as:
class AndhraBank(Bank):
The ‘cash’ variable and available_cash() methods are accessible to
AndhraBank class and
we can use them inside this sub class. Similarly, we can derive another sub
class by the
name StateBank from Bank class as:
class StateBank(Bank):
cash = 20000000 # class variable in the present sub class
@classmethod
def available_cash(cls):
print([Link] + [Link])
Here, StateBank has its own class variable ‘cash’ that contains 2 crores. So,
the total
cash available to StateBank is 10 crores + 2 crores = 12 crores. Please
observe the last
line in the preceding code:
print([Link] + [Link])
Here, ‘[Link]’ represents the current class’s ‘cash’ variable and ‘[Link]’
represents
the Bank base class ‘cash’ variable.
Program
Program 10: A Python program showing single inheritance in which two sub
classes are
derived from a single base class.
# single inheritance
class Bank(object):
cash = 100000000
@classmethod
def available_cash(cls):
print([Link])
class AndhraBank(Bank):
pass
class StateBank(Bank):
cash = 20000000
@classmethod
def available_cash(cls):
print([Link] + [Link])
a = AndhraBank()
a.available_cash()
s = StateBank()
s.available_cash()
Output:
C:\>python [Link]
100000000
120000000
Multiple Inheritance
Deriving sub classes from multiple (or more than one) base classes is called
‘multiple
inheritance’. In this type of inheritance, there will be more than one super
class and there
may be one or more sub classes. All the members of the super classes are by
default
available to sub classes and the sub classes in turn can have their own
members. The
syntax for multiple inheritance is shown in the following statement:
class Subclass(Baseclass1, Baseclass2, … ):
The best example for multiple inheritance is that parents producing the
children and the
children inheriting the qualities of the parents. Consider Figure 14.4.
Suppose, Father
and Mother are two base classes and Child is the sub class derived from
these two base
classes. Now, whatever the members are found in the base classes are
available to the
sub class. For example, the Father class has a method that displays his
height as 6.0
foot and the Mother class has a method that displays her color as brown. To
make the
Child class acquire both these qualities, we have to make it a sub class for
both the
Father and Mother class. This is shown in Program 11.

Program 11: A Python program to implement multiple inheritance using two


base
classes.
# multiple inheritance
class Father:
def height(self):
print('Height is 6.0 foot')
class Mother:
def color(self):
print('Color is brown')
class Child(Father, Mother):
pass
c = Child()
print('Child\'s inherited qualities: ')
[Link]()
[Link]()
Output:
C:\>python [Link]
Child's inherited qualities:
Height is 6.0 foot
Color is brown

Problems in Multiple Inheritance


If the sub class has a constructor, it overrides the super class constructor and
hence the
super class constructor is not available to the sub class. But writing
constructor is very
common to initialize the instance variables. In multiple inheritance, let’s
assume that a
sub class ‘C’ is derived from two super classes ‘A’ and ‘B’ having their own
constructors.
Even the sub class ‘C’ also has its constructor. To derive C from A and B, we
write:
class C(A, B):
Also, in class C’s constructor, we call the super class super class constructor
using
super().__init__(). Now, if we create an object of class C, first the class C
constructor is
called. Then super().__init__() will call the class A’s constructor. Consider
Program 12.
Program
Program 12: A Python program to prove that only one class constructor is
available to
sub class in multiple inheritance.
# when super classes have constructors
class A(object):
def __init__(self):
self.a = 'a'
print(self.a)
class B(object):
def __init__(self):
self.b = 'b'
print(self.b)
class C(A, B):
def __init__(self):
self.c = 'c'
print(self.c)
super().__init__()
# access the super class instance vars from C
o = C() # o is object of class C
Output:
C:\>python [Link]
c
a
The output of the program indicates that when class C object is created the
C’s
constructor is called. In class C, we used the statement: super().__init__()
that calls the
class A’s constructor only. Hence, we can access only class A’s instance
variables and not
that of class B. In Program 12, we created sub class C, as:
class C(A, B):
This means class C is derived from A and B as shown in the Figure 14.5.
Since all classes
are sub classes of object class internally, we can take classes A and B are
sub classes of
object class.

In the above figure, class A is at the left side and class B is at the right side
for the class
C. The searching of any attribute or method will start from the sub class C.
Hence, C’s
constructor is accessed first. As a result, it will display ‘c’. Observe the code
in C’s
constructor:
def __init__(self):
self.c = 'c'
print(self.c)
super().__init__()
The last line in the preceding code, i.e. super().__init__() will call the
constructor of the
class which is at the left side. So, class A’s constructor is executed and ‘a’ is
displayed. If
class A does not have a constructor, then it will call the constructor of the
right hand side
class, i.e. B. But since class A has a constructor, the search stopped here.
If the class C is derived as:
class C(B, A):
Then the output will be:
c
b
The problem we should understand is that the class C is unable to access
constructors of
both the super classes. It means C cannot access all the instance variables of
both of its
super classes. If C wants to access instance variables of both of its super
classes, then
the solution is to use super().__init__() in every class. This is shown in
Program 13.
Program
Program 13: A Python program to access all the instance variables of both
the base
classes in multiple inheritance.
# when super classes have constructors - v2.0
class A(object):
def __init__(self):
self.a = 'a'
print(self.a)
super().__init__()
class B(object):
def __init__(self):
self.b = 'b'
print(self.b)
super().__init__()
class C(A, B):
def __init__(self):
self.c = 'c'
print(self.c)
super().__init__()
# access the super class instance vars from C
o = C() # o is object of class C
Output:
C:\>python [Link]
c
a
b

We will apply the diagram given in Figure 14.5 to Program 13. The search will
start from
C. As the object of C is created, the constructor of C is called and ‘c’ is
displayed. Then
super().__init__() will call the constructor of left side class, i.e. of A. So, the
constructor of
A is called and ‘a’ is displayed. But inside the constructor of A, we again
called its super
class constructor using super().__init__(). Since ‘object’ is the super class for
A, an
attempt to execute object class constructor will be done. But object class
does not have
any constructor. So, the search will continue down to right hand side class of
object
class. That is class B. Hence B’s constructor is executed and ‘b’ is displayed.
After that
the statement super().__init__() will attempt to execute constructor of B’s
super class.
That is nothing but ‘object’ class. Since object class is already visited, the
search stops
here. As a result the output will be ‘c’, ’a’, ’b’. Searching in this manner for
constructors
or methods is called Method Resolution Order(MRO).
Method Resolution Order (MRO)
In the multiple inheritance scenario, any specified attribute or method is
searched first in
the current class. If not found, the search continues into parent classes in
depth-first,
left to right fashion without searching the same class twice. Searching in this
way is
called Method Resolution Order (MRO). There are three principles followed by
MRO.
 The first principle is to search for the sub class before going for its base
classes. Thus
if class B is inherited from A, it will search B first and then goes to A.
 The second principle is that when a class is inherited from several classes,
it searches
in the order from left to right in the base classes. For example, if class C is
inherited
from A and B as class C(A,B), then first it will search in A and then in B.
 The third principle is that it will not visit any class more than once. That
means a
class in the inheritance hierarchy is traversed only once exactly.
Understanding MRO gives us clear idea regarding which classes are executed
and in
which sequence. We can easily estimate the output when several base
classes are
involved. To know the MRO, we can use mro() method as:
[Link]()
This returns the sequence of execution of the classes, starting from the class
with which
the method is called. As depicted in Figure 14.6, we are going to create
inheritance
hierarchy with several classes. The sub class for all these classes is P. This is
shown in
Program 14.

Program 14: A Python program to understand the order of execution of


methods in
several base classes according to MRO.
# multiple inheritance with several classes
class A(object):
def method(self):
print('A class method')
super().method()
class B(object):
def method(self):
print('B class method')
super().method()
class C(object):
def method(self):
print('C class method')
class X(A, B):
def method(self):
print('X class method')
super().method()
class Y(B, C):
def method(self):
print('Y class method')
super().method()
class P(X,Y,C):
def method(self):
print('P class method')
super().method()
p = P()
[Link]()
Output:
C:\>python [Link]
P class method
X class method
A class method
Y class method
B class method
C class method

To understand the sequence of execution, we should apply MRO. The sub


class at the
bottom-most level is P. So, first P class method is executed (See output line
1). This class
is derived from 3 base classes in the order of X, Y, and C. Hence from left to
right, P’s first
base class X is searched (See output line 2). But, X is derived from 2 more
base classes in
the order of A, B. Hence, A is searched (See output line 3). Since A does not
have a userdefined super class, then it comes down to the class P’s second
base class, i.e. Y (See
output line 4). But Y is derived from two more base classes in the order B, C.
Hence from
left to right, it searches in B first (See output line 5). Since class B does not
have a userdefined super class, the search comes back to the 3rd base class
of class P, i.e. class C (output line 6).
If we use mro() method on class P as:
print([Link]())
It will display the following output which can be matched with our program
output:
[<class '__main__.P'>, <class '__main__.X'>, <class '__main__.A'>,
<class '__main__.Y'>, <class '__main__.B'>, <class '__main__.C'>, <class
'object'>]

Polymorphism
Polymorphism is a word that came from two Greek words, poly means many
and morphos
means forms. If something exhibits various forms, it is called polymorphism.
Let’s take a
simple example in our daily life. Assume that we have wheat flour. Using this
wheat flour,
we can make burgers, rotis, or loaves of bread. It means same wheat flour is
taking
different edible forms and hence we can say wheat flour is exhibiting
polymorphism.
Consider Figure 14.7:

In programming, a variable, object or a method will also exhibit the same


nature as that
of the wheat flour. A variable may store different types of data, an object
may exhibit
different behaviors in different contexts or a method may perform various
tasks in
Python. This type of behavior is called polymorphism. So, how can we define
polymorphism? If a variable, object or method exhibits different behavior in
different
contexts, it is called polymorphism. Python has built-in polymorphism. The
following
topics are examples for polymorphism in Python:
 Duck typing philosophy of Python
 Operator overloading
 Method overloading
 Method overriding
Duck Typing Philosophy of Python
We know that in Python, the data type of the variables is not explicitly
declared. This
does not mean that Python variables do not have a type. Every variable or
object in
Python has a type and the type is implicitly assigned depending on the
purpose for which
the variable is used. In the following examples, ‘x’ is a variable. If we store
integer into
that variable, its type is taken as ‘int’ and if we store a string into that
variable, its type is
taken as ‘str’. To check the type of a variable or object, we can use type()
function.
x = 5 # store integer into x
print(type(x)) # display type of x
<class 'int'>
x = 'Hello' # store string into x
print(type(x)) # display type of x
<class 'str'>
Python variables are names or tags that point to memory locations where
data is stored.
They are not worried about which data we are going to store. So, if ‘x’ is a
variable, we
can make it refer to an integer or a string as shown in the previous
examples. We can
conclude two points from this discussion:
1. Python’s type system is ‘strong’ because every variable or object has a
type that we
can check with the type() function.
2. Python’s type system is ‘dynamic’ since the type of a variable is not
explicitly
declared, but it changes with the content being stored.
Similarly, if we want to call a method on an object, we do not need to check
the type of
the object and we do not need to check whether that method really belongs
to that object
or not. For example, take a method call_talk() that accepts an object (or
instance) .
def call_talk(obj):
[Link]()
The call_talk() method is receiving an object ‘obj’ from outside and using this
object, it is
invoking (or calling) talk() method. It is not required to mention the type of
the object ‘obj’
or to check whether the talk() method belongs to that object or not. If the
object passed to
this method belongs to Duck class, then talk() method of Duck class is called.
If the
object belongs to Human class, then the talk() method of Human class is
called. This is
how Python understands. This is shown in Program 15.
Program
Program 15: A Python program to invoke a method on an object without
knowing the
type (or class) of the object.
# duck typing example
# Duck class contains talk() method
class Duck:
def talk(self):
print('Quack, quack!')
# Human class contains talk() method
class Human:
def talk(self):
print('Hello, hi!')
# this method accepts an object and calls talk() method
def call_talk(obj):
[Link]()
# call call_talk() method and pass an object
# depending on type of object, talk() method is executed
x = Duck()
call_talk(x)
x = Human()
call_talk(x)
Output:
C:\>python [Link]
Quack, quack!
Hello, hi!
The idea presented in Program 15 is that we do not need a type in order to
invoke an
existing method on an object. If the method is defined on the object, then it
can be called.
Thus, when we passed Duck object to call_talk(), it has called the talk()
method of Duck
type (i.e. Duck class). When we passed Human object to call_talk(), it has
called talk()
method of Human type.
During runtime, if it is found that the method does not belong to that object,
there will be
an error called ‘AttributeError’. This is shown in Program 16.
Program
Program 16: A Python program to call a method that does not appear in the
object
passed to the method.
# duck typing example - v2.0
# Dog class contains bark() method
class Dog:
def bark(self):
print('Bow, wow!')
# Duck class contains talk() method
class Duck:
def talk(self):
print('Quack, quack!')
# Human class contains talk() method
class Human:
def talk(self):
print('Hello, hi!')
# this method accepts an object and calls talk() method
def call_talk(obj):
[Link]()
# call call_talk() method and pass an object
# depending on type of object, talk() method is executed
x = Duck()
call_talk(x)
x = Human()
call_talk(x)
x = Dog()
call_talk(x) # ERROR occurs in this call
Output:
C:\>python [Link]
Quack, quack!
Hello, hi!
Traceback (most recent call alst):
File "[Link]", line 27, in <module>
call_talk(x)
File "[Link]", line 18, in call_talk
[Link]()
AttributeError: 'Dog' object has no attribute 'talk'
In Program 16, we are passing Dog class object to call_talk() method in the
last
statement. Using this object, call_talk() method called the talk() method as:
[Link]().
Since the object type is Dog, this call to talk() method tries to execute talk()
method of
Dog class which was not found. Hence there was an error.
In the preceding program, let’s observe the call_talk() method that accepts
an object ‘obj’
and calls talk() method on the object.
def call_talk(obj):
[Link]() # call talk() method of object
This method is calling talk() method of the object ‘obj’. It is not bothered
about which
class object it is. We can pass any class object as long as that object contains
the talk()
method. That means we can pass Duck object or Human object since they
contain the
talk() method. But when we pass the Dog object, there would be an error
since it does not
contain talk() method.
So in Python, we never worry about the type (class) of objects. The object
type is
distinguished only at runtime. If ‘it walks like a duck and talks like a duck, it
must be a
duck’ – this is the principle we follow. This is called duck typing. From the
previous
example, we can understand that the behavior of the talk() method is
changing
depending on the object type. This is an example for polymorphism of
methods.
We can rewrite Program 16 where we can check whether the object passed
to the
call_talk() method has the method that is being invoked or not. This is done
by rewriting
the method as:
def call_talk(obj):
if hasattr(obj, 'talk'): # if obj has talk() method then
[Link]() # call it on the object
elif hasattr(obj, 'bark'): # if obj has bark() method then
[Link]() # call it
In the preceding code, we are checking whether the object has a method or
not with the
help of hasattr() function. This function is written in the form of:
hasattr(object, attribute)
Here, ‘attribute’ may be a method or variable. If it is found in the object (i.e.
in the class
to which the object belongs) then this method returns True, else False.
Checking the
object type (or class) in this manner is called ‘strong typing’. Please
understand that this
is not duck typing.

Program 17: A Python program to check the object type to know whether
the method
exists in the object or not.
# strong typing example
# Dog class contains bark() method
class Dog:
def bark(self):
print('Bow, wow!')
# Duck class contains talk() method
class Duck:
def talk(self):
print('Quack, quack!')
# Human class contains talk() method
class Human:
def talk(self):
print('Hello, hi!')
# this method accepts an object and calls talk() method
def call_talk(obj):
if hasattr(obj, 'talk'):
[Link]()
elif hasattr(obj, 'bark'):
[Link]()
else:
print('Wrong object passed...')
# call call_talk() method and pass an object
# depending on type of object, talk() method is executed
x = Duck()
call_talk(x)
x = Human()
call_talk(x)
x = Dog()
call_talk(x)
Output:
C:\>python [Link]
Quack, quack!
Hello, hi!
Bow, wow!
Operator Overloading
We know that an operator is a symbol that performs some action. For
example, ‘+’ is an
operator that performs addition operation when used on numbers. When an
operator can
perform different actions, it is said to exhibit polymorphism

Program 18: A Python program to use addition operator to act on different


types of
objects.
# overloading the + operator
# using + on integers to add them
print(10+15)
# using + on strings to concatenate them
s1 = "Red"
s2 = "Fort"
print(s1+s2)
# using + on lists to make a single list
a = [10, 20, 30]
b = [5, 15, -10]
print(a+b)
Output:
C:\>python [Link]
25
RedFort
[10, 20, 30, 5, 15, -10]
In Program 18, the ‘+’ operator is first adding two integer numbers. Then the
same
operator is concatenating two strings. Finally, the same operator is
combining two lists of
elements and making a single list. In this way, if any operator performs
additional actions
other than what it is meant for, it is called operator overloading. Operator
overloading is
an example for polymorphism.
Normally, addition operator ‘+’ adds two numbers. For example, we can
write 10+15. But
we cannot use the addition operator to add two objects, as: obj1+obj2. Our
intention of
writing like this is to use the addition operator to add the data of these two
objects. This
is not possible. See the example program 19. In this program, we have two
classes
‘BookX’ and ‘BookY’. Each book has a number of pages which is given at the
time of
creating the objects as:
b1 = BookX(100)
b2 = BookY(150)
Now, if we write b1+b2, the addition operator cannot add the number of
pages which are
available in the objects since this operator cannot act on the objects.
Program
Program 19: A Python program to use addition operator to add the contents
of two
objects.
# Using + operator on objects
class BookX:
def __init__(self, pages):
[Link] = pages
class BookY:
def __init__(self, pages):
[Link] = pages
b1 = BookX(100)
b2 = BookY(150)
print('Total pages= ', b1+b2)
Output:
C:\>python [Link]
Traceback (most recent call last):
File "[Link]", line 12, in <module>
print('Total pages= ', b1+b2)
TypeError: unsupported operand type(s) for +: 'BookX' and 'BookY'
We can overload the ‘+’ operator to act upon the two objects and perform
addition
operation on the contents of the objects. That means we are giving
additional task to the
‘+’ operator. This comes under operator overloading.
Let’s go a bit into internal details. The ‘+’ operator is in fact internally written
as a special
method, i.e. __add__(). So, if we add two numbers by writing a+b, the
internal method is
called as: a.__add__(b). By overriding this method to act upon objects, we
can make the
‘+’ operator to successfully act on the objects also. Since we want to
override, we have to
write the method with same name but with objects as:
def __add__(self, other):
return self.a+other.b
Here, self.a represents the numeric content of first class and other.b
represents the
numeric content of second class. The preceding method should be written in
the first
class. To add the pages of BookX and BookY objects, we have to write this
method as:
def __add__(self, other):
return [Link]+[Link]
Program
Program 20: A Python program to overload the addition operator (+) to
make it act on
class objects.
# overloading + operator to act on objects
class BookX:
def __init__(self, pages):
[Link] = pages
def __add__(self, other):
return [Link]+[Link]
class BookY:
def __init__(self, pages):
[Link] = pages
b1 = BookX(100)
b2 = BookY(150)
print('Total pages= ', b1+b2)

Output:
C:\>python [Link]
Total pages= 250
Table 14.1 summarizes important operators and their corresponding internal
methods
that can be overridden to act on objects. These methods are called magic
methods

We will plan another program where we want to overload the greater than
(>) operator.
This operator is normally used on numbers to compare them. It returns True
of False
depending on the result. But if want to use it on objects, we have to overload
it. For this
purpose the magic method __gt__() should be overridden. For example, to
compare the
pages of two books, we can write this method as:
def __gt__(self, other):
return [Link]>[Link]
The preceding method returns True if the pages in first object is greater than
those of
second object, otherwise False.
Program
Program 21: A Python program to overload greater than (>) operator to
make it act on
class objects.
# overloading > operator
class Ramayan:
def __init__(self, pages):
[Link] = pages
def __gt__(self, other):
return [Link]>[Link]
class Mahabharat:
def __init__(self, pages):
[Link] = pages
b1 = Ramayan(1000)
b2 = Mahabharat(1500)
if(b1>b2):
print('Ramayan has more pages')
else:
print('Mahabharat has more pages')
Output:
C:\>python [Link]
Mahabharat has more pages
Another example is where we have an Employee class that contains the
name and daily
salary of the employee. Another class Attendance contains the employee
name and his
number of working days. To get the total salary of the employee we have to
multiply the
daily salary with the number of days worked. That means we have to
multiply the
Employee object data with Attendance object data. For this purpose, we
should overload
the multiplication operator.
Since multiplication operator is internally represented by the magic method
__mul__(), we
have to rewrite or override this method to make it act on the objects as:
def __mul__(self, other):
return [Link]*[Link]
When * operator is used on the objects, this method is called and the
required result is
obtained.

Program 22: A Python program to overload the multiplication (*) operator


to make it act
on objects.
# overloading the * operator
class Employee:
def __init__(self, name, salary):
[Link] = name
[Link] = salary
def __mul__(self, other):
return [Link]*[Link]
class Attendance:
def __init__(self, name, days):
[Link] = name
[Link] = days
x1 = Employee('Srinu', 500.00)
x2 = Attendance('Srinu', 25)
print('This month salary= ', x1*x2)
Output:
C:\>python [Link]
This month salary= 12500.0
Method Overloading
If a method is written such that it can perform more than one task, it is called
method
overloading. We see method overloading in the languages like Java. For
example, we call
a method as:
sum(10, 15)
sum(10, 15, 20)
In the first call, we are passing two arguments and in the second call, we are
passing
three arguments. It means, the sum() method is performing two distinct
operations:
finding sum of two numbers or sum of three numbers. This is called method
overloading.
In Java, to achieve this, we write two sum() methods with different number of
parameters
as:
sum(int a, int b) {}
sum(int a, int b, int c) {}
Since same name is given for these two methods, the user feels that the
same method is
performing the two operations. This is how the method overloading is done in
Java.
Method overloading is not available in Python. Writing more than one method
with the
same name is not possible in Python. So, we can achieve method
overloading by writing
same method with several parameters. The method performs the operation
depending on
the number of arguments passed in the method call. For example, we can
write a sum()
method with default value ‘None’ for the arguments as:

def sum(self, a=None, b=None, c=None):


if a!=None and b!=None and c!=None:
print('Sum of three= ', a+b+c)
elif a!=None and b!=None:
print('Sum of two= ', a+b)
Here, sum() has three arguments ‘a’,’b’,’c’ whose values by default are
‘None’. This ‘None’
indicates nothing or no value and similar to ‘null’ in languages like Java.
While calling
this method, if the user enters three values, then the arguments: ‘a’,’ b’ and
‘c’ will not be
‘None’. They get the values entered by the user. If the user enters only two
values, then
the first two arguments ‘a’ and ‘b’ only will take those values and the third
argument ‘c’
will become ‘None’. In this way, it is possible to find sum of either two
numbers or three
numbers.
Program
Program 23: A Python program to show method overloading to find sum of
two or three
numbers.
# method overloading
class Myclass:
def sum(self, a=None, b=None, c=None):
if a!=None and b!=None and c!=None:
print('Sum of three= ', a+b+c)
elif a!=None and b!=None:
print('Sum of two= ', a+b)
else:
print('Please enter two or three arguments')
# call sum() using object
m = Myclass()
[Link](10, 15, 20)
[Link](10.5, 25.55)
[Link](100)
Output:
C:\>python [Link]
Sum of three= 45
Sum of two= 36.05
Please enter two or three arguments
In Program 23, the sum() method is calculating sum of two or three numbers
and hence
it is performing more than one task. Hence it is an overloaded method. In
this way,
overloaded methods achieve polymorphism.
Method Overriding
We already discussed constructor overriding and method overriding under
inheritance
section. When there is a method in the super class, writing the same method
in the sub
class so that it replaces the super class method is called ‘method overriding’.
The
programmer overrides the super class methods when he does not want to
use them in
sub class. Instead, he wants a new functionality to the same method in the
sub class.

In inheritance, if we create super class object (or instance), we can access all
the
members of the super class but not the members of the sub class. But if we
create sub
class object, then both the super class and sub class members are available
since the
sub class object contains a copy of the super class. Hence, in inheritance we
always
create sub class object.
In Program 24, we created Square class with the area() method that
calculates the area of
the square. Circle class is a sub class to Square class that contains the area()
method
rewritten with code to calculate area of circle. When we create sub class
object as:
c = Circle() # create sub class object
[Link](15) # call area() method
Then the area() method of Circle class is called but not the area() method of
Square class.
The reason is that the area() method of sub class has overridden the area()
method of the
super class.
Program
Program 24: A Python program to override the super class method in sub
class.
# method overriding
import math
class Square:
def area(self, x):
print('Square area= %.4f}'% x*x)
class Circle(Square):
def area(self, x):
print('Circle area= %.4f'% ([Link]*x*x))
# call area() using sub class object
c = Circle()
[Link](15)
Output:
C:\>python [Link]
Circle area= 706.8583
Calling the area() method using Circle class object will execute Circle class
area() method.
But in case, the programmer wants to calculate the area of the square, he
can call the
same area() method using the Square class object. So, same area() method
is performing
two different tasks depending on the object type. This is an example for
polymorphism.

ABSTRACT CLASSES AND INTERFACES


Program 1: A Python program to understand that Myclass method is shared
by all of its
objects.
# A class with a method
class Myclass:
def calculate(self, x):
print('Square value= ', x*x)
# all objects share same calculate() method
obj1 = Myclass()
[Link](2)
obj2 = Myclass()
[Link](3)
obj3 = Myclass()
[Link](4)

Output:
C:\>python [Link]
Square value= 4
Square value= 9
Square value= 16
Of course, in the preceding program, the requirement of all the objects is
same, i.e., to
calculate square value. Then this program is alright. But, sometimes the
requirement of
the objects will be different and entirely dependent on the specific object
only. For
example, in the preceding program, if the first object wants to calculate
square value, the
second object wants the square root value and the third object wants cube
value. In such
a case, how to write the calculate() method in Myclass?
Since, the calculate() method has to perform three different tasks depending
on the
object, we cannot write the code to calculate square value in the body of
calculate()
method. On the other hand, if we write three different methods like
calculate_square(),
calculate_sqrt(), and calculate_cube() in Myclass, then all the three methods
are available
to all the three objects which is not advisable. When each object wants one
method,
providing all the three does not look reasonable. To serve each object with
the one and
only required method, we can follow the steps:
1. First, let’s write a calculate() method in Myclass. This means every object
wants to
calculate something.
2. If we write body for calculate() method, it is commonly available to all the
objects. So
let’s not write body for calculate() method. Such a method is called abstract
method.
Since, we write abstract method in Myclass, it is called abstract class.
3. Now derive a sub class Sub1 from Myclass, so that the calculate() method
is available
to the sub class. Provide body for calculate() method in Sub1 such that it
calculates
square value. Similarly, we create another sub class Sub2 where we write the
calculate() method with body to calculate square root value. We create the
third sub
class Sub3 where we write the calculate() method to calculate cube value.
This
hierarchy is shown in Figure 15.1.
4. It is possible to create objects for the sub classes. Using these objects, the
respective
methods can be called and used. Thus, every object will have its requirement
fulfilled.

Abstract Method and Abstract Class


An abstract method is a method whose action is redefined in the sub classes
as per the
requirement of the objects.
Generally abstract methods are written without body since their body will be
defined in the sub classes anyhow.
But it is possible to write an abstract method with body also. To mark a
method as abstract, we should use the decorator @abstractmethod. On the
other hand, a concrete method is a method with body.
An abstract class is a class that generally contains some abstract methods.
Since,
abstract class contains abstract methods whose implementation (or body) is
later defined
in the sub classes, it is not possible to estimate the total memory required to
create the
object for the abstract class.
So, PVM cannot create objects to an abstract class.
Once an abstract class is written, we should create sub classes and all the
abstract
methods should be implemented (body should be written) in the sub classes.
Then, it is
possible to create objects to the sub classes.
In Program 2, we create Myclass as an abstract super class with an abstract
method
calculate(). This method does not have any body within it. The way to create
an abstract
class is to derive it from a meta class ABC that belongs to abc (abstract base
class)
module as:
class Abstractclass(ABC):

Since all abstract classes should be derived from the meta class ABC which
belongs to
abc (abstract base class) module, we should import this module into our
program.
A meta class is a class that defines the behavior of other classes. The meta
class ABC defines that the class which is derived from it becomes an abstract
class.

To import abc module’s


ABC class and abstractmethod decorator we can write as follows:

from abc import ABC, abstractmethod


or
from abc import *

Now, our abstract class ‘Myclass’ should be derived from the ABC class as:
classs Myclass(ABC):
@abstractmethod
def calculate(self, x):
pass # empty body, no code

Observe the preceding code. Our Myclass is abstract class since it is derived
from ABC
meta class. This class has an abstract method calculate() that does not
contain any code.
We used @abstractmethod decorator to specify that this is an abstract
method. We have
to write sub classes where this abstract method is written with its body (or
implementation). In Program 2, we are going to write three sub classes:
Sub1, Sub2 and
Sub3 where this abstract method is implemented as per the requirement of
the objects.
Since, the same abstract method is implemented differently for different
objects, they can
perform different tasks.

Program 2: A Python program to create abstract class and sub classes


which implement
the abstract method of the abstract class.
# abstract class example
from abc import ABC, abstractmethod
class Myclass(ABC):
@abstractmethod
def calculate(self, x):
pass # empty body, no code
# this is sub class of Myclass
class Sub1(Myclass):
def calculate(self, x):
print('Square value= ', x*x)
# this is another sub class for Myclass
import math
class Sub2(Myclass):
def calculate(self, x):
print('Square root= ', [Link](x))
# third sub class for Myclass
class Sub3(Myclass):
def calculate(self, x):
print('Cube value= ', x**3)
# create Sub1 class object and call calculate() method
obj1 = Sub1()
[Link](16)
# create Sub2 class object and call calculate() method
obj2 = Sub2()
[Link](16)
# create Sub3 class object and call calculate() method
obj3 = Sub3()
[Link](16)

Output:
C:\>python [Link]
Square value= 256
Square root= 4.0
Cube value= 4096

In the preceding program, the same calculate() method written in the


abstract class is
implemented differently in the three sub classes and it is able to perform
different tasks.
Since the same method is performing various tasks, it is coming under
polymorphism.
Let’s take another example to understand the abstract class concept in a
better way. We
see many cars on the road. These cars are all objects of Car class. For
example, Maruti,
Santro, Benz are all objects of Car class. Suppose, we plan to write Car class,
it contains
all the attributes (variables) and actions (methods) of any car object in the
world. For
example, we can write the following members in Car class:

Registration number: Every car will have a registration number and hence
we write
this as an instance variable in Car class. All cars whether it is Maruti or
Santro
should have a registration number. It means registration number is a
common
feature to all the objects. So, it can be written as an instance variable in the
Car
class.

Fuel tank: Every car will have a fuel tank, opening and filling the tank is an
action.
To represent this action, we can write a method like: openTank()
How do we open and fill the tank? Take the key, open the tank and fill fuel.
Let’s
assume that all cars have same mechanism of opening the tank and filling
fuel. So,
the code representing the opening mechanism can be written in openTank()
method’s
body. So it becomes a concrete method. A concrete method is a method with
body.

Steering: Every car will have a steering wheel and steering the car is an
action. For
this, we write a method as: steering()
How do we steer the car? All the cars do not have same mechanism for
steering.
Maruti cars have manual steering. Santro cars have power steering. So, it is
not
possible to write a particular mechanism in steering() method. So, this
method
should be written without body in Car class. Thus, it becomes an abstract
method.

Brakes: Every car will have brakes. Applying brakes is an action and hence
it can be
represented as a method, as: braking()
How do we apply brakes? All cars do not have same mechanism for brakes.
Maruti
cars have hydraulic brakes. Santro cars have gas brakes. So, we cannot write
a
particular braking mechanism in this method. This method, hence, will not
have a
body in Car class, and hence becomes abstract. See Figure 15.2:

So, the Car class has an instance variable, one concrete method and two
abstract
methods. Hence, Car class will become abstract class. See this class in
Program 3 given
here.
Program
Program 3: A Python program to create a Car abstract class that contains an
instance
variable, a concrete method and two abstract methods.
# This is an abstract class. Save this code as [Link]
from abc import *
class Car(ABC):
def __init__(self, regno):
[Link] = regno
def openTank(self):
print('Fill the fuel into the tank')
print('for the car with regno ', [Link])
@abstractmethod
def steering(self):
pass
@abstractmethod
def braking(self):
pass
Output:
C:\>python [Link]
C:\>
Now that we have written the abstract class, the next step is to derive sub
classes from
the Car class. In the sub classes, we should take the abstract methods of the
Car class
and implement (writing body in) them. The reason why we implement the
abstract
methods in the sub classes is that the implementation of these methods is
dependent on
the sub classes. In our program, let’s write Maruti and Santro as the two sub
classes
where the two abstract methods will be implemented accordingly. See these
sub classes
in Programs 4 and 5.
Program
Program 4: A Python program in which Maruti sub class implements the
abstract
methods of the super class, Car.
# this is a sub class for abstract Car class
from abs import Car
class Maruti(Car):
def steering(self):
print('Maruti uses manual steering')
print('Drive the car')
def braking(self):
print('Maruti uses hydraulic brakes')
print('Apply brakes and stop it')
# create object to Maruti and use its features
m = Maruti(1001)
[Link]()
[Link]()
[Link]()
Output:
C:\>python [Link]
Fill the fuel into the tank
for the car with regno 1001
Maruti uses manual steering
Drive the car
Maruti uses hydraulic brakes
Apply brakes and stop it
Program
Program 5: A Python program in which Santro sub class implements the
abstract
methods of the super class, Car.
# this is a sub class for abstract Car class
from abs import Car
class Santro(Car):
def steering(self):
print('Santro uses power steering')
print('Drive the car')
def braking(self):
print('Santro uses gas brakes')
print('Apply brakes and stop it')
# create object to Santro and use its features
s = Santro(7878)
[Link]()
[Link]()
[Link]()

Output:
C:\>python [Link]
Fill the fuel into the tank
for the car with regno 7878
Santro uses power steering
Drive the car
Santro uses gas brakes
Apply brakes and stop it

An ordinary class can be called rigid class since it can look after the common
needs of
the objects. There is no scope for catering the individual requirements of
objects. Abstract
classes are more flexible and useful than ordinary classes since they cater
the common
needs of the objects as well as their individual needs also.

Interfaces in Python
We learned that an abstract class is a class which contains some abstract
methods as
well as concrete methods also. Imagine there is a class that contains only
abstract
methods and there are no concrete methods. It becomes an interface. This
means an
interface is an abstract class but it contains only abstract methods. None of
the methods
in the interface will have body. Only method headers will be written in the
interface. So
an interface can be defined as a specification of method headers. Since, we
write only
abstract methods in the interface, there is possibility for providing different
implementations (body) for those abstract methods depending on the
requirements of
objects. In the languages like Java, an interface is created using the key word
‘interface’
but in Python an interface is created as an abstract class only. The interface
concept is
not explicitly available in Python. We have to use abstract classes as
interfaces in Python.
Since an interface contains methods without body, it is not possible to create
objects to
an interface. In this case, we can create sub classes where we can
implement all the
methods of the interface. Since the sub classes will have all the methods
with body, it is
possible to create objects to the sub classes. The flexibility lies in the fact
that every sub
class can provide its own implementation for the abstract methods of the
interface.
Since, none of the methods have body in the interface, we may tend to think
that writing
an interface is mere waste. This is not correct. In fact, an interface is more
useful when
compared to the class owing to its flexibility of providing necessary
implementation
needed by the objects. Let’s elucidate this point further with an example. We
have some
rupees in our hands. We can spend in rupees only by going to a shop where
billing is
done in rupees. Suppose we have gone to a shop where only dollars are
accepted, we
cannot use our rupees there. This money is like a ‘class’. A class satisfies the
only
requirement intended for it. It is not useful to handle a different situation.
Suppose we have an international credit card. Now, we can pay by using our
credit card
in rupees in a shop. If we go to another shop where they expect us to pay in
dollars, we
can pay in dollars. The same credit card can be used to pay in pounds also.
Here, the
credit card is like an interface which performs several tasks. In fact, the
credit card is a
plastic card and does not hold any money physically. It contains just our
name, our bank
name and perhaps some number. But how the shop keepers are able to draw
the money
from the credit card? Behind the credit card, we got our bank account which
holds the
money from where it is transferred to the shop keepers. This bank account
can be taken
as a sub class which actually performs the task. See Figure 15.3:

Let’s see how the interface concept is advantageous in software


development. A
programmer is asked to write a Python program to connect to a database
and retrieve the
data, process the data and display the results in the form of some reports.
For this
purpose, the programmer has written a class to connect to Oracle database,
something
like this:
# this class works with Oracle database only
class Oracle:
def connect(self):
print('Connecting to Oracle database...')
def disconnect(self):
print('Disconnected from Oracle.')
This class has a limitation. It can connect only to Oracle database. If a client
(user) using
any other database (for example, Sybase database) uses this code to
connect to his
database, this code will not work. So, the programmer is asked to design his
code in such
a way that it is used to connect to any database in the world. How is it
possible?
One way is to write several classes, each to connect to a particular database.
Thus,
considering all the databases available in the world, the programmer has to
write a lot of
classes. This takes a lot of time and effort. Even though the programmer
spends a lot of
time and writes all the classes, by the time the software is released into the
market, all
the versions of the databases will change and the programmer is supposed
to rewrite the
classes again to suit the latest versions of databases. This is very
cumbersome.

Interface helps to solve this problem. The programmer writes an interface


‘Myclass’ with
abstract methods as shown here:

# an interface to connect to any database


class Myclass(ABC):
@abstractmethod
def connect(self):
pass
@abstractmethod
def disconnect(self):
pass

We cannot create objects to the interface. So, we need sub classes where all
these
methods of the interface are implemented to connect to various databases.
This task is
left for other companies that are called third party vendors. The third party
vendors will
provide sub classes to Myclass interface. For example, Oracle Corp people
may provide a
sub class where the code related to connecting to the Oracle database and
disconnecting
from the database will be provided as:

# this is a sub class to connect to Oracle


class Oracle(Myclass):
def connect(self):
print('Connecting to Oracle database...')
def disconnect(self):
print('Disconnected from Oracle.')
Note that ‘Oracle’ is a sub class of Myclass interface. Similarly, the Sybase
company
people may provide another implementation class ‘Sybase’, where code
related to
connecting to Sybase database and disconnecting from Sybase will be
provided as:
# this is another sub class to connect to Sybase
class Sybase(Myclass):
def connect(self):
print('Connecting to Sybase database...')
def disconnect(self):
print('Disconnected from Sybase.')

Now, it is possible to create objects to the sub classes and call the connect()
method and
disconnect() methods from a main program. This main program is also
written by the
same programmer who develops the interface. Let’s understand that the
programmer who
develops the interface does not know the names of the sub classes as they
will be
developed in future by the other companies. In the main program, the
programmer is
supposed to create objects to the sub classes without knowing their names.
This is done
using globals() function.

First, the programmer should accept the database name from the user. It
may be ‘Oracle’
or ‘Sybase’. This name should be taken in a string, say ‘str’. The next step is
to convert
this string into a class name using the built-in function globals(). The
globals() function
returns a dictionary containing current global names and globals()[str]
returns the name
of the class that is in 'str'. Hence, we can get the class name as:
classname = globals()[str]

Now, create an object to this class and call the methods as:
x = classname() # x is object of the class
[Link]()
[Link]()
The connect() method establishes connection with the particular database
and
disconnect() method disconnects from the database. The complete program
is shown in
Program 6.

Program 6: A Python program to develop an interface that connects to any


database.
# abstract class works like an interface
from abc import *
class Myclass(ABC):
@abstractmethod
def connect(self):
pass
@abstractmethod
def disconnect(self):
pass
# this is a sub class
class Oracle(Myclass):
def connect(self):
print('Connecting to Oracle database...')
def disconnect(self):
print('Disconnected from Oracle.')
# this is another sub class
class Sybase(Myclass):
def connect(self):
print('Connecting to Sybase database...')
def disconnect(self):
print('Disconnected from Sybase.')
class Database:
# accept database name as a string
str = input('Enter database name: ')
# convert the string into classname
classname = globals()[str]
# create an object to that class
x = classname()
# call the connect() and disconnect() methods
[Link]()
[Link]()
Output:
C:\>python [Link]
Enter database name: Oracle
Connecting to Oracle database...
Disconnected from Oracle.

C:\>python [Link]
Enter database name: Sybase
Connecting to Sybase database...
Disconnected from Sybase.

When an interface is created, it is not necessary for the programmer to


provide the sub
classes for the interface. Any third party vendors can do that. The client or
user
purchases and uses the interface and the sub class depending on his
requirements. If he
wants to connect to Oracle, he will purchase Oracle sub class. If he wants to
connect to
Sybase, he will purchase Sybase sub class.
The question is how the third party vendors know which methods they should
write in
the sub classes? In this case, API documentation will help them. An API
(Application
Programming Interface) documentation file is a text file or html file that
contains
description of all the features of software, language or a product. After
developing the
software, the programmer creates API documentation file that contains
description of all
the classes, methods and attributes. This file is referred by the third party
vendors to
know about the interface and its methods. Then they write the same
methods in the sub
classes. This entire discussion is represented in Figure 15.4:

Let’s take another example where interface is used. We want to write Printer
interface
which is used to send data to different printers. This interface has a method
printit() that
sends text to the printer and another method disconnect() that disconnects
the printer
after printing is done. Of course, this program is a model how the printer
interface can be
used. It does not send the text to a real printer. On the other hand, it displays
the text on
the screen.
Printer interface is implemented by IBM people such that it sends text to IBM
printer.
Similarly, Epson people provide a different implementation to the Printer
interface such
that it sends text to Epson printer. These sub classes are written as IBM and
Epson
classes.

To use a printer, first of all we should know which printer is used by the
client. We
assume that the printer name is generally stored in the [Link] file at the
time of
installing the printer driver software. So, open notepad (or any text editor)
and create a
file with the name [Link] and store a single line that represents the
printer driver
name as:
Epson
And then save the file. Alternately, we can store the name IBM in the
[Link] file. The
name of the printer available in the [Link] file can be read from the file
using
readline() method as:
with open("[Link]", "r") as f:
str = [Link]() # read printer name from f and store into str
This reads one line from the file containing the string ‘Epson’ which was
already stored
by us in the [Link] file. This ‘Epson’ would appear in ‘str’ as a string.
Retrieving the
class name from this ‘str’ is done using globals() method. Then we create an
object to that
class and use it.
Program
Program 7: A Python program which contains a Printer interface and its sub
classes to
send text to any printer.
# An interface to send text to any printer
from abc import *
# create an interface
class Printer(ABC):
@abstractmethod
def printit(self, text):
pass
@abstractmethod
def disconnect(self):
pass
# this is sub class for IBM printer
class IBM(Printer):
def printit(self, text):
print(text)
def disconnect(self):
print('Printing completed on IBM printer.')
# this is sub class for Epson printer
class Epson(Printer):
def printit(self, text):
print(text)
def disconnect(self):
print('Printing completed on Epson printer.')
class UsePrinter:
# accept printer name as a string from configuration file
with open("[Link]", "r") as f:
str = [Link]()
# convert the string into classname
classname = globals()[str]
# create an object to that class
x = classname()
# call the printit() and disconnect() methods
[Link]('Hello, this is sent to printer')
[Link]()
Output:
C:\>python [Link]
Hello, this is sent to printer
Printing completed on Epson printer.
Please remember that we should run this program after creating the
[Link] file. If we
created the [Link] file with the string ‘IBM’, then the output of the
preceding program
would be:
Hello, this is sent to printer
Printing completed on IBM printer.
We can observe that the same printit() and disconnect() methods when
called are
performing different tasks in different contexts. They are sending text to
Epson printer or
IBM printer depending upon the user requirement. This is an example for
polymorphism.
Let’s understand that the abstract classes and interfaces are examples for
polymorphic
behavior.
Abstract Classes vs. Interfaces
Python does not provide interface concept explicitly. It provides abstract
classes which
can be used as either abstract classes or interfaces. It is the discretion of the
programmer
to decide when to use an abstract class and when to go for an interface.
Generally,
abstract class is written when there are some common features shared by all
the objects
as they are. For example, take a class WholeSaler which represents a whole
sale shop
with text books and stationery like pens, papers and note books as:
# an abstract class
class WholeSaler(ABC):
@abstractmethod
def text_books(self):
pass
@abstractmethod
def stationery(self):
pass
Let’s take Retailer1, a class which represents a retail shop. Retailer1 wants
text books of
X class and some pens. Similarly, Retailer2 also wants text books of X class
and some
papers. In this case, we can understand that the text_books() is the common
feature
shared by both the retailers. But the stationery asked by the retailers is
different. This
means, the stationery has different implementations for different retailers
but there is a
common feature, i.e., the text books. So in this case, the programmer
designs the
WholeSaler class as an abstract class. Retailer1 and Retailer2 are sub
classes.

On the other hand, the programmer uses an interface if all the features need
to be
implemented differently for different objects. Suppose, Retailer1 asks for VII
class text
books and Retailer2 asks for X class text books, then even the text_books()
method of
WholeSaler class needs different implementations depending on the retailer.
It means,
the text_books() method and also stationery() methods should be
implemented differently
depending on the retailer. So, in this case, the programmer designs the
WholeSaler as an
interface and Retailer1 and Retailer2 become sub classes.
There is a responsibility for the programmer to provide the sub classes
whenever he
writes an abstract class. This means the same development team should
provide the sub
classes for the abstract class. But if an interface is written, any third party
vendor will
take the responsibility of providing sub classes. This means, the programmer
prefers to
write an interface when he wants to leave the implementation part to the
third party
vendors.
In case of an interface, every time a method is called, PVM should search for
the method
in the implementation classes which are installed elsewhere in the system
and then
execute the method. This takes more time. But when an abstract class is
written, since
the common methods are defined within the abstract class and the sub
classes are
generally in the same place along with the software, PVM will not have that
much
overhead to execute a method. Hence, interfaces are slow when compared
to abstract
classes.
EXCEPTIONS

A software developer is also a human being and hence prone to commit


errors either in the design of the software or in writing the code.
The errors in the software are called ‘bugs’ and the process of removing
them is called ‘debugging’.
Let’s learn about different types of errors that can occur in a program.
Errors in a Python Program
In general, we can classify errors in a program into one of these three types:
 Compile-time errors
 Runtime errors
 Logical errors

Compile-Time Errors
These are syntactical errors found in the code, due to which a program fails
to compile.
For example, forgetting a colon in the statements like if, while, for, def, etc.
will result in
compile-time error. Such errors are detected by Python compiler and the line
number
along with error description is displayed by the Python compiler. Let’s see
Program 1 to
understand this better. In this program, we have forgotten to write colon in
the if
statement, after the condition. This will raise SyntaxError.

Program 1: A Python program to understand the compile-time error.


# example for compile-time error
x=1
if x == 1
print('Where is colon?')
Output:
C:\>python [Link]
File "[Link]", line 3
if x == 1
^
SyntaxError: invalid syntax
We know that Python statements are written in blocks using proper
indentation. The
default number of spaces used for indentation is 4. All the statements
belonging to the
same block should use same number of spaces before them. If there is any
deviation in
the spaces, then we can see IndentationError raised by Python compiler.
Consider
Program 2 where we are using unequal number of spaces for the print
statements inside
the if statement.

Program 2: A Python program to demonstrate compile-time error.


# another compile-time error
x = 10
if x%2==0:
print(x,' is divisible by 2')
print(x,' is even number')
Output:
C:\>python [Link]
File "[Link]", line 5
print(x,' is even number')
^
IndentationError: unexpected indent
The Python compiler displays error message and the line number in case of
compile-time
errors. By checking the program source code and rewriting the statements
properly, we
can eliminate the compile-time errors.
Runtime Errors
When PVM cannot execute the byte code, it flags runtime error. For example,
insufficient
memory to store something or inability of the PVM to execute some
statement come
under runtime errors. Runtime errors are not detected by the Python
compiler. They are
detected by the PVM, only at runtime. The following program explains this
further:

Program 3: A Python program to understand runtime errors.


# example for runtime error
def concat(a, b):
print(a+b)
# callconcat() and pass arguments
concat('Hai', 25)
Output:
C:\>python [Link]
Traceback (most recent call last):
File "[Link]", line 6, in <module>
concat('Hai', 25)
File "[Link]", line 3, in concat
print(a+b)
TypeError: Can't convert 'int' object to str implicitly.
In Program 3, we have written a function by the name ‘concat’ that accepts 2
arguments
‘a’ and ‘b’. It adds them using ‘+’ operator and displays the result. At the
time of calling
this function, if we pass two strings, they will be concatenated or joined. In
this case, ‘+’
acts like concatenation operator. On the other hand, if we pass 2 numbers,
then they are
added and result is displayed. In this case, ‘+’ acts as addition operator. But,
in the above
example, we are passing one string and one number. Since the datatypes are
not same,
PVM shows ‘TypeError’. In Python, compiler will not check the datatypes. Type
checking
is done by PVM during runtime.
Program 4 is also an example for runtime error. In this program, we are
creating a list
with 4 elements. The indexes (or position numbers) of these elements will be
from 0 to 3.
When we refer to the index 4 which is not in the list, there will be IndexError
during
runtime.
Program
Program 4: A Python program to demonstrate runtime error.
# another runtime error
animal = ['Dog', 'Cat', 'Horse', 'Donkey']
print(animal[4])
Output:
C:\>python [Link]
Traceback (most recent call last:
File "[Link]", line 3, in <module>
print(animal[4])
IndexError: list index out of range
In case of runtime error, the PVM displays the line number and the type of
error. Most of
the runtime errors can be eliminated by following the message given by
PVM. For
example, in the previous program, restricting the list index below 4 is the
solution to
eliminate the runtime error. But some runtime errors cannot be eliminated. In
that case,
we should handle those errors using ‘exception handling mechanism’ of
Python.

Logical Errors
These errors depict flaws in the logic of the program. The programmer might
be using a
wrong formula or the design of the program itself is wrong. Logical errors are
not detected
either by Python compiler or PVM. The programmer is solely responsible for
them. In the
following program, the programmer wants to calculate incremented salary of
an
employee, but he gets wrong output, since he uses wrong formula.

Program 5: A Python program to increment the salary of an employee by


15%.
# logical error
def increment(sal):
sal = sal * 15/100
return sal
# call increment() and pass salary
sal = increment(5000.00)
print('Incremented salary= %.2f' %sal)
Output:
C:\>python [Link]
Incremented salary= 750.00
By comparing the output of a program with manually calculated results, a
programmer
can guess the presence of a logical error. In Program 5, we are using the
following
formula to calculate the incremented salary:
sal = sal * 15/100
This is wrong since this formula calculates only the increment but it is not
adding it to
the original salary. So, the correct formula would be:
sal = sal + sal * 15/100
Compile time errors and logical errors can be eliminated by the programmer
by modifying
the program source code. In case of runtime errors, when the programmer
knows which
type of error occurs, he has to handle them using exception handling
mechanism. The
runtime errors which can be handled by the programmer are called
exceptions. Before
discussing exception handling mechanism, we will first understand what type
of harm an
exception can cause. Program 6 will help us in this regard.
Program
Program 6: A Python program to understand the effect of an exception.
# an exception example
# open a file
f = open("myfile", "w")
# do some processing on the file
# accept a, b values, store the result of a/b into the file
a, b = [int(x) for x in input("Enter two numbers: ").split()]
c = a/b
[Link]("writing %d into myfile" %c)
# close the file
[Link]()
print('File closed')
Output:
C:\>python [Link]
Enter two numbers: 10 2
File closed

When we execute Program 6, it opens a file by the name “myfile” using


open() method,
and then writes the result of a/b into that file using write() method. Finally,
the file is
closed using close() method. This program runs well if we give ‘a’ and ‘b’
values from the
keyboard as: 10 and 2. Since a/b value is 5, this value (i.e. 5) is stored into
the file. After
that, the file is closed.
What happens if we enter 10 and 0 as values for ‘a’ and ‘b’ in the previous
program. Let’s
see:
C:\>python [Link]
Enter two numbers: 10 0
Traceback (most recent call last):
File "[Link]", line 8, in <module>
c = a/b
ZeroDivisionError: division by zero
Since a/b represents 10/0 that gives infinity which is a huge quantity that
cannot be
stored into any variable, we are getting an error ‘ZeroDivisionError’. When
this error
occurred, PVM is simply displaying the error message and immediately
terminating the
program in line number 8. Due to this abnormal termination, the subsequent
statements
in the program are not executed. Hence, [Link]() is not executed and the file
which is
opened in the beginning of the program is not closed. This leads to loss of
entire data that
is already present in the file. A file that is opened in any mode should be
closed properly.
This ensures safety for the data present in the file.
So, when there is an error in a program, due to its sudden termination, the
following
things can be suspected:
 The important data in the files or databases used in the program may be
lost.
 The software may be corrupted.
 The program abruptly terminates giving error message to the user making
the user
losing trust in the software.
Hence, it is the duty of the programmer to handle the errors. Please
understand that we
cannot handle all errors. We can handle only some types of errors which are
called
exceptions.

Exceptions
An exception is a runtime error which can be handled by the programmer.
That means if
the programmer can guess an error in the program and he can do something
to eliminate
the harm caused by that error, then it is called an ‘exception’. If the
programmer cannot
do anything in case of an error, then it is called an ‘error’ and not an
exception.
All exceptions are represented as classes in Python. The exceptions which
are already
available in Python are called ‘built-in’ exceptions. The base class for all built-
in
exceptions is ‘BaseException’ class. From BaseException class, the sub class
‘Exception’
is derived.
From Exception class, the sub classes ‘StandardError’ and ‘Warning’ are
derived.
All errors (or exceptions) are defined as sub classes of StandardError. An
error should be
compulsorily handled otherwise the program will not execute. Similarly, all
warnings are
derived as sub classes from ‘Warning’ class. A warning represents a caution
and even
though it is not handled, the program will execute. So, warnings can be
neglected but
errors cannot be neglected.
Just like the exceptions which are already available in Python language, a
programmer
can also create his own exceptions, called ‘user-defined’ exceptions. When
the
programmer wants to create his own exception class, he should derive his
class from
Exception class and not from ‘BaseException’ class. In Figure 16.1, we are
showing
important classes available in Exception hierarchy:

Exception Handling
The purpose of handling errors is to make the program robust. The word
‘robust’ means
‘strong’. A robust program does not terminate in the middle. Also, when
there is an error
in the program, it will display an appropriate message to the user and
continue execution. Designing such programs is needed in any software
development. For this
purpose, the programmer should handle the errors. When the errors can be
handled,
they are called exceptions.
To handle exceptions, the programmer should perform the following three
steps:
Step 1: The programmer should observe the statements in his program
where there may
be a possibility of exceptions. Such statements should be written inside a
‘try’ block. A
try block looks like as follows:
try:
statements

The greatness of try block is that even if some exception arises inside it, the
program will
not be terminated. When PVM understands that there is an exception, it
jumps into an
‘except’ block.

Step 2: The programmer should write the ‘except’ block where he should
display the
exception details to the user. This helps the user to understand that there is
some error
in the program. The programmer should also display a message regarding
what can be
done to avoid this error.

Except block looks like as follows:


except exceptionname:
statements # these statements form handler

The statements written inside an except block are called ‘handlers’ since
they handle the
situation when the exception occurs.

Step 3: Lastly, the programmer should perform clean up actions like closing
the files and
terminating any other processes which are running. The programmer should
write this
code in the finally block. Finally block looks like as follows:
finally:
statements

The specialty of finally block is that the statements inside the finally block
are executed
irrespective of whether there is an exception or not. This ensures that all the
opened files
are properly closed and all the running processes are properly terminated.
So, the data in
the files will not be corrupted and the user is at the safe-side.

Performing the above 3 tasks is called ‘exception handling’. Remember, in


exception
handling, the programmer is not preventing the exception, as in many cases
it is not
possible. But the programmer is avoiding any damage that may happen to
data and
software. Let’s rewrite program 6 to handle the ZeroDivisionError exception
using try,
except and finally blocks.

Program 7: A Python program to handle the ZeroDivisionError exception.


# an exception handling example
try:
f = open("myfile", "w")
a, b = [int(x) for x in input("Enter two numbers: ").split()]
c = a/b
[Link]("writing %d into myfile" %c)
except ZeroDivisionError:
print('Division by zero happened')
print('Please do not enter 0 in input')
finally:
[Link]()
print('File closed')
Output:
C:\>python [Link]
Enter two numbers: 10 2
File closed
C:\>python [Link]
Enter two number: 10 0
Division by zero happened
Please do not enter 0 in input
File closed
From the preceding output, we can understand that the ‘finally’ block is
executed and the
file is closed in both the cases, i.e. when there is no exception and when the
exception
occurred. In the previous discussion, we used try-catch-finally to handle the
exception.
However, the complete exception handling syntax will be in the following
format:
try:
statements
except Exception1:
handler1
except Exception2:
handler2
else:
statements
finally:
statements
The ‘try’ block contains the statements where there may be one or more
exceptions. The
subsequent ‘except’ blocks handle these exceptions. When ‘Exception1’
occurs, ‘handler1’
statements are executed. When ‘Exception2’ occurs, ‘hanlder2’ statements
are executed
and so forth. If no exception is raised, the statements inside the ‘else’ block
are executed.
Even if the exception occurs or does not occur, the code inside ‘finally’ block
is always
executed. The following points are noteworthy:
A single try block can be followed by several except blocks.
Multiple except blocks can be used to handle multiple exceptions.
We cannot write except blocks without a try block.
We can write a try block without any except blocks.
Else block and finally blocks are not compulsory.
When there is no exception, else block is executed after try block.
Finally block is always executed.

Types of Exceptions
There are several exceptions available as part of Python language that are
called built-in
exceptions. In the same way, the programmer can also create his own
exceptions called
user-defined exceptions. Table 16.1 summarizes some important built-in
exceptions in
Python. Most of the exception class names end with the word ‘Error’.
We will now see how to work with built-in exceptions. In Program 8, we are
trying to
handle SyntaxError that is raised by eval() function. The eval() function
accepts input in
the form of a list, tuple or dictionary and evaluates the input properly. In this
program,
we are entering date in the form of year, month and date separating them by
commas,
e.g. 2016, 10, 3. When a group of values are entered separating them by
commas, they
are understood as a tuple by eval() function. While entering these values any
letter is by
mistake typed along with the value, there will be SyntaxError raised by the
eval()
function. This error is caught in except block and a message ‘Invalid date
entered’ is
displayed.

Program 8: A Python program to handle syntax error given by eval()


function.
# example for syntax error
try:
date = eval(input("Enter date: "))
except SyntaxError:
print('Invalid date entered')
else:
print('You entered: ', date)
Output:
C:\>python [Link]
Enter date: 2016, 10, 3
You entered: (2016, 10, 4)
Enter date: 2016, 10b, 3
Invalid date entered
In the next program, we will accept a file name from the keyboard and then
open it using
open() function. If the file is not found, then IOError is raised. Then ‘except’
block will display a message: ‘File not found’. If file is found, then all the lines
of the file are read
using readlines() method as:
n = len([Link]())
Here, [Link]() will read the lines available in the file. Then len() function
will return
their number. This is stored into ‘n’.

Program 9: A Python program to handle IOError produced by open()


function.
# example for IOError
# accept a filename
try:
name = input('Enter filename: ')
f = open(name, 'r')
except IOError:
print('File not found: ', name)
else:
n = len([Link]())
print(name, 'has', n, 'lines')
[Link]()
Output:
C:\>python [Link]
Enter filename: [Link]
[Link] has 11 lines
C:\>python [Link]
Enter filename: abcd
File not found: abcd
In Program 10, we are defining a function avg() to find total and average of
list of
numbers. It returns total and average as:
return tot, avg
We can call this function and get these values into two variables ‘t’ and ‘a’
as:
t,a = avg([1,2,3,4,5]) # call avg() and pass list of 5 elements.
In this case, the output will be:
Total= 15, Average= 3.0
But, a list can contain different types of elements. What happens if we give a
list that
contains some strings as:
t,a = avg([1,2,3,4,5, ‘a’]) # call avg() and pass list of 6
#elements.
Since the last element is ‘a’ that cannot be added to other elements, avg()
function cannot
find total and average. It raises an exception by the name ‘TypeError’. On the
other hand,
if we pass an empty list, then the number of elements becomes zero and
while calculating
average as total / n, there will be ‘ZeroDivisionError’. We can handle these
two
exceptions using two except blocks as shown in the program.

Program 10: A Python program to handle multiple exceptions.


# example for two exceptions
# a function to find total and average of list elements
def avg(list):
tot=0
for x in list:
tot+=x
avg = tot/len(list)
return tot, avg
# call the avg() and pass a list
try:
t,a = avg([1,2,3,4,5,'a']) # here, give empty list and try.
print('Total= {}, Average= {}'.format(t,a))
except TypeError:
print('Type Error, please provide numbers. ')
except ZeroDivisionError:
print('ZeroDivisionError, please do not give empty list. ')

Output:
C:\>python [Link]
Type Error, please provide numbers.
In the previous program, if we call avg() function and pass empty list as:
t,a = avg([1,2,3,4,5,'a'])
Then the following will be the output:
ZeroDivisionError, please do not give empty list.

The Except Block


The ‘except’ block is useful to catch an exception that is raised in the try
block. When
there is an exception in the try block, then only the except block is executed.
It is written
in various formats.

1. To catch the exception which is raised in the try block, we can write except
block with
the Exceptionclass name as:
except Exceptionclass:

2. We can catch the exception as an object that contains some description


about the
exception.
except Exceptionclass as obj:

3. To catch multiple exceptions, we can write multiple catch blocks. The other
way is to
use a single except block and write all the exceptions as a tuple inside
parentheses
as:
except (Exceptionclass1, Exceptionclass2, … ):

4. To catch any type of exception where we are not bothered about which
type of
exception it is, we can write except block without mentioning any
Exceptionclass
name as:
except:

In the previous Program 10, we are catching two exceptions using two except
blocks. The
same can be written using a single except block as:
except (TypeError, ZeroDivisionError):
print('Either TypeError or ZeroDivisionError occurred. ')
The other way is not writing any exception name in except block. This will
catch any type
of exception, but the programmer cannot determine specifically which
exception has
occurred. For example,
except:
print('Some exception occurred. ')
In Program 11, we are finding inverse of a given number. In this program, we
are using
try block without except block. When we want to use try block alone, we
need to follow it
with a finally block. Since we are not using except block, it is not possible to
catch the
exception.

Program 11: A Python program to understand the usage of try with finally
blocks.
# try without except block
try:
x = int(input('Enter a number: '))
y=1/x
finally:
print("We are not catching the exception.")
print("The inverse is: ", y)
Output:
C:\>python [Link]
Enter a number: 5
We are not catching the exception.
The inverse is: 0.2
The assert Statement
The assert statement is useful to ensure that a given condition is True. If it is
not true, it
raises AssertionError. The syntax is as follows:
assert condition, message
If the condition is False, then the exception by the name AssertionError is
raised along
with the ‘message’ written in the assert statement. If ‘message’ is not given
in the assert
statement, and the condition is False, then also AssertionError is raised
without
message. In Program 12, we are using assert statement without a message.
If the
condition mentioned in the assert statement is False, thenAssertionError is
raised.

Program 12: A Python program using the assert statement and catching
AssertionError.
# handling AssertionError
try:
x = int(input('Enter a number between 5 and 10: '))
assert x>=5 and x<=10
print('The number entered: ', x)
except AssertionError:
print('The condition is not fulfilled')
Output:
C:\>python [Link]
Enter a number between 5 and 10: 12
The condition is not fulfilled
The same program can be rewritten using a message after the condition in
the assert
statement. When the condition is False, the message is passed to
AssertionError object
‘obj’ that can be displayed in the except block as shown in Program 13.

Program 13: A Python program to use the assert statement with a


message.
# handling AssertionError - v 2.0
try:
x = int(input('Enter a number between 5 and 10: '))
assert x>=5 and x<=10, "Your input is not correct"
print('The number entered: ', x)
except AssertionError as obj:
print(obj)
Output:
C:\>python [Link]
Enter a number between 5 and 10: 12
Your input is not correct

User-Defined Exceptions
Like the built-in exceptions of Python, the programmer can also create his
own
exceptions which are called ‘User- defined exceptions’ or ‘Custom
exceptions’. We know
Python offers many exceptions which will raise in different contexts. For
example, when a
number is divided by zero, the ZeroDivisionError is raised. Similarly, when
the datatype
is not correct, TypeErroris raised.
But, there may be some situations where none of the exceptions in Python
are useful for
the programmer. In that case, the programmer has to create his own
exception and raise
it. For example, let’s take a bank where customers have accounts. Each
account is
characterized by customer name and balance amount. The rule of the bank
is that every
customer should keep minimum Rs. 2000.00 as balance amount in his
account. The
programmer now is given a task to check the accounts to know every
customer is maintaining minimum balance of Rs. 2000.00 or not. If the
balance amount is below Rs.
2000.00, then the programmer wants to raise an exception saying ‘Balance
amount is
less in the account of so and so person’. This will be helpful to the bank
authorities to
find out the customer.

So, the programmer wants an exception that is raised when the balance
amount in an
account is less than Rs 2000.00. Since there is no such exception available in
Python,
the programmer has to create his own exception. For this purpose, he has to
follow these
steps:

1. Since all exceptions are classes, the programmer is supposed to create his
own
exception as a class. Also, he should make his class as a sub class to the in-
built
‘Exception’ class.
class MyException(Exception):
def __init__(self, arg):
[Link] = arg

Here, ‘MyException’ class is the sub class for ‘Exception’ class. This class has
a
constructor where a variable ‘msg’ is defined. This ‘msg’ receives a message
passed
from outside through ‘arg’.
2. The programmer can write his code; maybe it represents a group of
statements or a
function. When the programmer suspects the possibility of exception, he
should raise
his own exception using ‘raise’ statement as:
raise MyException(‘message')
Here, raise statement is raising MyException class object that contains the
given
‘message’.
3. The programmer can insert the code inside a ‘try’ block and catch the
exception using
‘except’ block as:
try:
code
except MyException as me:
print(me)

Here, the object ‘me’ contains the message given in the raise statement. All
these steps
are shown in Program 14. In this program, we are passing a dictionary with
names and
balances to the check() function. As dictionary elements, name is the key
and balance is
the value. The check() function displays these details and checks whether
the balance is
less than 2000.00. If it is so, then it raises MyException with a message:
'Balance
amount is less in the account of so and so person’. This message is passed to
‘except’
block where it is displayed.

Program 14: A Python program to create our own exception and raise it
when needed.
# create our own class as sub class to Exception class
class MyException(Exception):
def __init__(self, arg):
[Link] = arg

# write code where exception may raise


# to raise the exception, use raise statement
def check(dict):
for k,v in [Link]():
print('Name= {:15s} Balance= {:10.2f}'.format(k,v))
if(v<2000.00):
raise MyException('Balance amount is less in the account of
'+k)
# our own exception is handled using try and except blocks
bank = {'Raj':5000.00, 'Vani':8900.50, 'Ajay':1990.00,
'Naresh':3000.00}
try:
check(bank)
except MyException as me:
print(me)

Output:
C:\>python [Link]
Name=Raj Balance=5000.00
Name= Vani Balance=8900.50
Name= Ajay Balance=1990.00
Balance amount is less in the account of Ajay

Logging the Exceptions


It is a good idea to store all the error messages raised by a program into a
file. The file
which stores the messages, especially of errors or exceptions is called a ‘log’
file and this
technique is called ‘logging’. When we store the messages into a log file, we
can open the
file and read it or take a print out of the file later. This helps the
programmers to
understand how many errors are there, the names of those errors and where
they are
occurring in the program. This information will enable them to pin point the
errors and
also rectify them easily. So, logging helps in debugging the programs.
Python provides a module ‘logging’ that is useful to create a log file that can
store all error
messages that may occur while executing a program.
There may be different levels of error messages. For example, an error that
crashes the
system should be given more importance than an error that merely displays
a warning
message. So, depending on the seriousness of the error, they are classified
into 6 levels
in ‘logging’ module, as shown in Table 16.2:

As we know, by default, the error messages that occur at the time of


executing a program
are displayed on the user’s monitor. Only the messages which are equal to or
above the
level of a WARNING are displayed. That means WARNINGS, ERRORS and
CRITICAL
ERRORS are displayed. It is possible that we can set this default behavior as
we need.
To understand different levels of logging messages, we are going to write a
Python
program. In this program, first we have to create a file for logging (storing)
the messages.
This is done using basicConfig() method of logging module as:
[Link](filename=’[Link]’, level=[Link])
Here, the log file name is given as ‘[Link]’. The level is set to ERROR.
Hence the
messages whose level will be at ERROR or above, (i.e. ERROR or CRITICAL)
will only be
stored into the log file. Once, this is done, we can add the messages to the
‘[Link]’
file as:
[Link](‘message’)
The methodnames can be critical(), error(), warning(), info() and debug(). For
example, we
want to add a critical message, we should use critical() method as:
[Link](‘System crash - Immediate attention required’)
Now, this error message is stored into the log file, i.e. ‘[Link]’. Observe
Program 15.

Program 15: A Python program that creates a log file with errors and critical
messages.
# understanding logging of error messages.
import logging
# store messages into [Link] file.
# store only the messages with level equal to or more than that of ERROR
[Link](filename='[Link]', level=[Link])
# these messages are stored into the file.
[Link]("There is an error in the program.")
[Link]("There is a problem in the design.")
# but these are not stored.
[Link]("The project is going slow.")
[Link]("You are a junior programmer.")
[Link]("Line no. 10 contains syntax error.")

Output:
C:\>python [Link]
C:\>
When the above program is executed, we can see a file created by the name
‘[Link]’ in
our current directory. Open the file to see the following messages:
ERROR:root:There is an error in the program.
CRITICAL:root:There is a problem in the design.
In Program 15, we imported logging module as:
import logging

Since only the module is imported, we have to refer to its methods using this
module
name, as: [Link](), [Link](), etc. To avoid writing the
module name
before the methods, we can change the import statement as:

from logging import *

Here, we are importing all methods ( * means all) from the logging module.
That means,
instead of importing the logging module, we are importing its methods.
Hence, we can
refer to the methods directly, without using the module name. We can refer
to them
simply as: basicConfig(), error(), etc.
Logging can be used to store all the exception messages which occur in a
program. For
this purpose, we should use exception() method to send messages to the log
file. But this
exception() method should be always used inside ‘except’ block only. For
example, to
store the exception message which is in ‘e’ into the log file, we can write the
‘except’ block
as:

except Exception as e: # exception message will be in the object ‘e’


[Link](e) # store that message into log file

In Program 16, we are accepting two numbers ‘a’ and ‘b’ from the user and
then finding
the result of their division. When an exception occurs inside ‘try’ block, the
‘except’ block
will catch it and the exception message will be stored into the object ‘e’. This
message is
then written into the log file ‘[Link]’.

Program 16: A Python program to store the messages released by any


exception into a
log file.
# logging all messages from a program
import logging
# store logging messages into [Link] file
[Link](filename='[Link]', level=[Link])
try:
a = int(input('Enter a number: '))
b = int(input('Enter another number: '))
c = a/b
except Exception as e:
[Link](e)

else:
print('The result of division: ', c)

Output:
C:\>python [Link]
Enter a number: 10
Enter another number: 20
The result of division: 0.5
C:\>python [Link]
Enter a number: 10
Enter another number: 0
C:\>python [Link]
Enter a number: 10
Enter another number: ab

Please observe that the program is executed 3 times with different inputs.
First time, the
values supplied are 10 and 20. With these values, the program executed well
and there
are no exceptions. Second time, the values supplied are 10 and 0. In this
case, there is
possibility for ZeroDivisionError. The message related to this exception will
be stored into
our log file. In the third time execution, the values entered are 10 and ‘ab’. In
this case,
there is possibility for ValueError. The message of this exception will also be
added to our
log file. Now, we can open the log file ‘[Link]’ and see the following
messages:
ERROR:root:division by zero
Traceback (most recent call last):
File "[Link]", line 9, in <module>
c = a/b
ZeroDivisionError: division by zero
ERROR:root:invalid literal for int() with base 10: 'ab'
Traceback (most recent call last):
File "[Link]", line 8, in <module>
b = int(input('Enter another number: '))
ValueError: invalid literal for int() with base 10: 'ab'

REGULAR EXPRESSIONS IN PYTHON

Many a times, we are needed to extract required information from given


data. For
example, we want to know the number of people who contacted us in the
last
month through Gmail or we want to know the phone numbers of employees
in a
company whose names start with ‘A’ or we want to retrieve the date of births
of the
patients in a hospital who joined for treatment for hypertension, etc.
To get such information, we have to conduct the searching operation on the
data. Once we get the required information, we have to extract that data for
further use.
Regular expressions are useful to perform such operations on data.

Regular Expressions
A regular expression is a string that contains special symbols and characters
to find and
extract the information needed by us from the given data.
A regular expression helps us to search information, match, find and split
information as per our requirements.
A regular expression is also called simply regex. Regular expressions are
available not only
in Python but also in many languages like Java, Perl, AWK, etc.
Python provides re module that stands for regular expressions. This module
contains
methods like compile(), search(), match(), findall(), split(), etc. which are
used in finding
the information in the available data.
So, when we write a regular expression, we should import re module as:
import re

Regular expressions are nothing but strings containing characters and


special symbols.
A simple regular expression may look like this:
reg = r'm\w\w'
In the preceding line, the string is prefixed with ‘r’ to represent that it is a
raw string.
Generally, we write regular expressions as raw strings. Let’s understand why
this is so.
When we write a normal string as:
str = 'This is normal\nstring'
Now, print(str) will display the preceding string in two lines as:
This is normal
string
Thus the ‘\n’ character is interpreted as new line in the normal string by the
Python
interpreter and hence the string is broken there and shown in the new line. In
regular
expressions when ‘\n’ is used, it does not mean to throw the string into new
line. There
‘\n’ has a different meaning and it should not be interpreted as new line. For
this
purpose, we should take this as a ‘raw’ string. This is done by prefixing ‘r’
before the
string.
str = r'This is raw\nstring'
When we display this string using print(str), the output will be:
This is raw\nstring
So, the normal meaning of ‘\n’ is escaped and it is no more an escape
character in the
preceding example.
Since ‘\n’ is not an escape character, it is interpreted as a character with
different meaning in the regular expression by the Python interpreter.

Similarly, the characters like ‘\t’, ‘\w’, ‘\c’, etc. should be interpreted as
special characters in the regular expressions and hence the expressions
should be written as raw strings.
If we do not want to write the regular expressions as raw strings, then the
alternative is to use another backslash before such characters. For example,
we can write:
reg = r'm\w\w' # as raw string
reg = 'm\\w\\w' # as normal string

But using backslashes like this may be confusing for the programmer. Now,
let’s go back
to our first regular expression:
reg = r'm\w\w'
This expression is written in single quotes to represent that it is a string. The
first
character ‘m’ represents that the words starting with ‘m’ should be matched.
The next
character ‘\w’ represents any one character in A to Z, a to z and 0 to 9. Since
we used
two ‘\w’ characters, they represent any two characters after ‘m’. So, this
regular
expression represents words or strings having three characters and with ‘m’
as first
character. The next two characters can be any alphanumeric.
Yes, we developed our first regular expression! The next step is to compile
this expression
using compile() method of ‘re’ module as:
prog = [Link](r'm\w\w')

Now, prog represents an object that contains the regular expression. The
next step is to
run this expression on a string ‘str’ using the search() method or match()
method as:
str = 'cat mat bat rat' # this is the string on which regular
# expression will act
result = [Link](str) # searching for regular expression in str
The result is stored in ‘result’ object and we can display it by calling the
group() method
on the object as:
print([Link]())
mat
This is how a regular expression is created and used. We write all the steps
at one place
to have better idea:
import re
prog = [Link](r'm\w\w')
str = 'cat mat bat rat'
result = [Link](str)
print([Link]())
mat
In the preceding code, the regular expression after compilation is available in
prog object.
So, we need not compile the expression again and again when we want to
use the same
expression on other strings. This will improve the speed of execution. For
example, let’s
use the same regular expression on a different string as:
str1 = 'Operating system format'
result = [Link](str1)
print([Link]())
mat
Figure 18.1 shows how to execute the regular expressions in the Python IDLE
window:
Instead of compiling the first regular expression and then running the next
one, we can
use a single step to compile and run all the regular expression as:
result = [Link](r'm\w\w', str)
The preceding code is equivalent to:
prog = [Link](r'm\w\w')
result = [Link](str)
So, the general form of writing regular expressions is as follows:
result = [Link](‘expression’, ‘string’)
In the following program, the same regular expression is used on a different
string.
Consider Program 1.

Program 1: A Python program to create a regular expression to search for


strings starting
with m and having total 3 characters using the search() method.
import re
str = 'man sun mop run'
result = [Link](r'm\w\w', str)
if result: # if result is not None
print([Link]())
Output:
C:\>python [Link]
man
Observe the output of Program 1. The search() method searches for the
strings according
to the regular expression and returns only the first string. This is the reason
that even
though there are two strings ‘man’ and ‘mop’, it returned only the first one.
This string
can be extracted using the group() method. In case, the search() method
could not find
any strings matching the regular expression, then it returns None. So, to
display the
result, we can use either of the statements given here:
if result is not None:
print([Link]())
if result: # if result is not None
print([Link]())
Suppose, we want to get all the strings that match the pattern mentioned in
the regular
expression, we should use findall() method instead of search() method. The
findall()
method returns all resultant strings into a list. This is shown in Program 2.

Program 2: A Python program to create a regular expression to search for


strings starting
with m and having total 3 characters using the findall() method.
import re
str = 'man sun mop run'
print(result)
Output:
C:\>python [Link]
['man', 'mop']
In Program 2, the findall() method returned the result as a list. The elements
of the list
can be displayed using a for loop as:
for s in result:
print(s)
There is another method by the name match() that returns the resultant
string only if it is
found in the beginning of the string. The match() method will give None if the
string is not
in the beginning. Let’s consider Program 3 and Program 4.

Program 3: A Python program to create a regular expression using the


match() method to
search for strings starting with m and having total 3 characters.
import re
str = 'man sun mop run'
result = [Link](r'm\w\w', str)
print([Link]())
Output:
C:\>python [Link]
man

Program 4: A Python program to create a regular expression using the


match() method to
search for strings starting with m and having total 3 characters.
import re
str = 'sun man mop run'
result = [Link](r'm\w\w', str)
print(result)
Output:
C:\>python [Link]
None

There is a method split() that splits the given string into pieces according to
the regular
expression and returns the pieces as elements of a list. Suppose we write a
regular
expression as:
[Link](r'\W+', str)
Observe the regular expression ‘\W’. This is capital ‘W’ which is reverse to
the small ‘w’ so
far used. ‘w’ represents any one alpha numeric character, i.e. A-Z, a-z, 0-9.
But ‘W’
represents any character that is not alpha numeric. So, the work of this
regular
expression is to split the string ‘str’ at the places where there is no alpha
numeric

result is that the string will be split into pieces where 1 or more non alpha
numeric
characters are found. Consider Program 5.

Program 5: A Python program to create a regular expression to split a string


into pieces
where one or more non alpha numeric characters are found.
import re
str = 'This; is the: "Core" Python\'s book'
result = [Link](r'\W+', str)
print(result)
Output:
C:\>python [Link]
['This', 'is', 'the', 'Core', 'Python', 's', 'book']
Sometimes, regular expressions can also be used to find a string and then
replace it with
a new string. For this purpose, we should use the sub() method of ‘re’
module. The format
of this method is:
sub(regular expression, new string, string)
For example, sub('Ahmedabad', 'Allahabad', str) will replace ‘Ahmedabad’
with ‘Allahabad’
in the string ‘str’. Consider Program 6.

Program 6: A Python program to create a regular expression to replace a


string with a
new string.
import re
str = 'Kumbhmela will be conducted at Ahmedabad in India.'
res = [Link](r'Ahmedabad', 'Allahabad', str)
print(res)
Output:
C:\>python [Link]
Kumbhmela will be conducted at Allahabad in India.
So, regular expressions are used to perform the following important
operations:
 Matching strings
 Searching for strings
 Finding all strings
 Splitting a string into pieces
 Replacing strings

The following methods belong to the ‘re’ module that are used in the regular
expressions:
 The match() method searches in the beginning of the string and if the
matching string
is found, it returns an object that contains the resultant string, otherwise it
returns
None. We can access the string from the returned object using group()
method.
 The search() method searches the string from beginning till the end and
returns the
first occurrence of the matching string, otherwise it returns None. We can
use group()
method to retrieve the string from the object returned by this method.
 The findall() method searches the string from beginning till the end and
returns all
occurrences of the matching string in the form of a list object. If the
matching strings
are not found, then it returns an empty list. We can retrieve the resultant
strings
from the list using a for loop.
 The split() method splits the string according to the regular expression and
the
resultant pieces are returned as a list. If there are no string pieces, then it
returns an
empty list. We can retrieve the resultant string pieces from the list using a
for loop.
 The sub() method substitutes (or replaces) new strings in the place of
existing strings.
After substitution, the main string is returned by this method.

Sequence Characters in Regular Expressions


Sequence characters match only one character in the string. Let’s list out the
sequence
characters which are used in regular expressions along with their meanings
in
Table 18.1:

Each of these sequence characters represents a single character matched in


the string.
For example, ‘\w’ indicates any one alphanumeric character. Suppose we
write it as
[\w]*. Here ‘*’ represents 0 or more repetitions. Hence [\w]* represents 0 or
more
alphanumeric characters.
Let’s write a regular expression to retrieve all words starting with ‘a’. This
can be written
as:
r'a[\w]*'
Here, ‘a’ represents the word should start with ‘a’. Then [\w]* represents
repetition of any
alphanumeric characters. Consider Program 7.
Program
Program 7: A Python program to create a regular expression to retrieve all
words starting
with a in a given string.
import re
str = 'an apple a day keeps the doctor away'
result = [Link](r'a[\w]*', str)
# findall() returns a list, retrieve the elements from list
for word in result:
print(word)
Output:
C:\>python [Link]
an
apple
a
ay
away
Please observe the output. It contains ‘ay’ which is not a word. This ‘ay’ is
part of the
word ‘away’. So, it is displaying both ‘ay’ and ‘away’ as they are starting with
‘a’. We do
not want like this. We want only the words starting with ‘a’. Since a word will
have a
space in the beginning or ending, we can use ‘\b’ before and after the words
in the
regular expression. So, the regular expression will become:
result = [Link](r'\ba[\w]*\b', str)
This will retrieve only the word and not the part of the words. So, the output
will be:
an
apple
a
away

Program 8 is an attempt to retrieve all the words starting with a numeric digit
like 0, 1, 2
or 9. The numeric digit is represented by ‘\d’ and hence, the expression will
be:
r'\d[\w]*'.

Program 8: A Python program to create a regular expression to retrieve all


words starting
with a numeric digit.
import re
str = 'The meeting will be conducted on 1st and 21st of every month'
result = [Link](r'\d[\w]*', str)
for word in result:
print(word)
Output:
C:\>python [Link]
1st
21st

In Program 9, we are trying to retrieve all words having 5 characters length.


The
expression can be written something like this: r'\b\w{5}\b'. The character ‘\b’
represents
a space. We used this character in the beginning and ending of the
expression so that we
get the words surrounded by spaces. \w{5} represents a word containing
any
alphanumeric characters repeated for 5 times. A character in curly braces,
e.g. {m}
represents repetition for m times.

Program 9: A Python program to create a regular expression to retrieve all


words having
5 characters length.
import re
str = 'one two three four five six seven 8 9 10'
result = [Link](r'\b\w{5}\b', str)
print(result)
Output:
C:\>python [Link]
['three', 'seven']
In Program 9, instead of using the findall() method, if we use the search()
method, it will
return the first occurrence of the result only.

Program 10: A Python program to create a regular expression to retrieve all


words having
5 characters length using search().
# search() will give the first matching word only.
import re
str = 'one two three four five six seven 8 9 10'
# to retrieve the word from result object, use group()
print([Link]())
Output:
C:\>python [Link]
three
We will improve our search a little bit further. Now, we want to find all words
which are
at least 4 characters long. That means words with 4, 5 or any number of
characters will
be retrieved. For this purpose, we can write the regular expression as: r'\b\
w{4,}\b'.
Observe the number 4 and a comma in curly braces. This represents 4 or
above number
of characters.

Program 11: A Python program to create a regular expression to retrieve all


the words
that are having the length of at least 4 characters.
import re
str = 'one two three four five six seven 8 9 10'
result = [Link](r'\b\w{4,}\b', str)
print(result)
Output:
C:\>python [Link]
['three', 'four', 'five', 'seven']
Similarly, the program 12 helps us to retrieve all words with 3 to 5 characters
length.
Observe the curly braces with 3, 5 as: {3,5} that indicate 3 to 5 number of
characters.

Program 12: A Python program to create a regular expression to retrieve all


words with 3
or 4 or 5 characters length.
import re
str = 'one two three four five six seven 8 9 10'
result = [Link](r'\b\w{3,5}\b', str)
print(result)
Output:
C:\>python [Link]
['one', 'two', 'three', 'four', 'five', 'six', 'seven']
We know ‘\d’ represents a numeric digit (0 to 9). So, if we use it as: r'\b\d\b',
it
represents single digits in the string amidst of spaces.

Program 13: A Python program to create a regular expression to retrieve


only single
digits from a string.
import re
str = 'one two three four five six seven 8 9 10'
result = [Link](r'\b\d\b', str)
print(result)
Output:
C:\>python [Link]
['8', '9']

Suppose in the preceding program, we write the regular expression as: r'\b\d\
d\b', it
retrieves double digits (like 10, 02, 89 etc.) from the string.
‘\A’ is useful to match the words at the beginning of a string. Similarly, ‘\Z’ is
useful to
match the words at the end of a string. For example, we want to find whether
a string
contains at its end a word starting with ‘t’ or not. We can write an expression
as:
r't[\w]*\Z'. Here, ‘t’ represents that the word should start with ‘t’. [\w]*
represents any
characters after ‘t’. The last ‘\Z’ represents searching should be done at the
ending of the
string. Consider Program 14.

Program 14: A Python program to create a regular expression to retrieve


the last word of
a string, if it starts with t.
import re
str = 'one two three one two three'
result = [Link](r't[\w]*\Z', str)
print(result)
Output:
C:\>python [Link]
['three']

Quantifiers in Regular Expressions


In regular expressions, some characters represent more than one character
to be
matched in the string. Such characters are called ‘quantifiers’. For example,
if we write ‘+’
it represents 1 or more repetitions of the preceding character. Hence, if we
write an
expression as: r'\d+', this indicates that all numeric digits which occur for 1
or more
times should be extracted. Table 18.2 shows quantifiers available in Python:
In the next program, we are going to retrieve the phone number of a person
from a string
using the regular expression: r'\d+'.
Program
Program 15: A Python program to create a regular expression to retrieve
the phone
number of a person.
import re
str = 'Nageswara Rao: 9706612345'
res = [Link](r'\d+', str)
print([Link]())
Output:
C:\>python [Link]
9706612345
In Program 15, suppose we are asked to retrieve the person’s name and not
his phone
number. Now, how to do that? Very simple. Instead of writing ‘\d’, we use ‘\D’
in the
regular expression as ‘\D’ represents all characters except numeric
characters. [See
Table 18.1]

Program 16: A Python program to create a regular expression to extract


only name but
not number from a string.
import re
str = 'Nageswara Rao: 9706612345'
res = [Link](r'\D+', str)
print([Link]())
Output:
C:\>python [Link]
Nageswara Rao:
The special character ‘+’ represents 1 or more repetitions. Similarly, ‘*’
represents 0 or
more repetitions. Suppose, we want to write a regular expression that finds
all words
staring with either ‘an’ or ‘ak’, then we can use: r'a[nk][\w]*'. Here, observe
a[nk]. This
represents either ‘n’ or ‘k’ or both after ‘a’.

Program 17: A Python program to create a regular expression to find all


words starting
with ‘an’ or ‘ak’.
import re
str = 'anil akhil anant arun arati arundhati abhijit ankur'
res = [Link](r'a[nk][\w]*', str)
print(res)
Output:
C:\>python [Link]
['anil', 'akhil', 'anant', 'ankur']
We know that {m,n} indicates m to n occurrences. Suppose we take ‘\
d{1,3}’, it represents
1 to 3 occurrences of ‘\d’. Let’s see how to use this. We have a string that
contains
names, id numbers and date of births as:
str = 'Vijay 20 1-5-2001, Rohit 21 22-10-1990, Sita 22 15-09-2000'
Now, we want to retrieve only date of births of the candidates. We can write
a regular
expression as: r'\d{2}-\d{2}-\d{4}'. This retrieves only numeric digits in the
format of
2digits-2digits-4digits. Hence this can be used to retrieve the date of births
as shown in
Program 18.
Program
Program 18: A Python program to create a regular expression to retrieve
date of births
from a string.
import re
str = 'Vijay 20 1-5-2001, Rohit 21 22-10-1990, Sita 22 15-09-2000'
res = [Link](r'\d{2}-\d{2}-\d{4}', str)
print(res)
Output:
C:\>python [Link]
['22-10-1990', '15-09-2000']
Please observe that the date of birth of Vijay, i.e. 1-5-2001 is not retrieved.
The reason is
the date and month here have only one digit. But our regular expression
retrieves only if
there are 2 digits. So, how to solve this problem? We can modify the regular
expression
such that it retrieves either 1 or 2 digits in the date or month as:
res = [Link](r'\d{1,2}-\d{1,2}-\d{4}', str)
print(res)
['1-5-2001', '22-10-1990', '15-09-2000']
Special Characters in Regular Expressions
Characters with special significance shown in Table 18.3 can be used in
regular
expressions. These characters will make our searching easy.
Table 18.3: Special Characters in Regular Expressions

The carat (^) symbol is useful to check if a string is starting with a sub string
or not. For
example, to know a string is starting with ‘He’ or not, we can write the
expression: r"^He".
This is shown in Program 19.

Program 19: A Python program to create a regular expression to search


whether a given
string is starting with ‘He’ or not.
import re
str = "Hello World"
res = [Link](r"^He", str)
if res:
print ("String starts with 'He'")
else:
print("String does not start with 'He'")
Output:
C:\>python [Link]
String starts with 'He'
Similarly, to know whether a string is ending with a word, we can use dollar
($) symbol.
Suppose we write an expression as r"World$". This indicates a search for
World in the
ending of the main string, as shown in Program 20.

Program 20: A Python program to create a regular expression to search for


a word at the
ending of a string.
import re
str = "Hello World"
res = [Link](r"World$", str)
if res:
print ("String ends with 'World'")
else:
print("String does not end with 'World'")

Output:
C:\>python [Link]
String ends with 'World'
Regular expressions conduct a case sensitive searching for the strings.
Hence, in
Program 20, if we use the expression with a small ‘w’ as: r"world$", then we
will end up
with wrong output as:
String does not end with 'World'
We can specify a case insensitive matching of strings with the help of
IGNORECASE
constant of ‘re’ module. This is shown in Program 21.

Program 21: A Python program to create a regular expression to search at


the ending of
a string by ignoring the case.
import re
str = "Hello World"
res = [Link](r"world$", str, [Link])
if res:
print ("String ends with 'World'")
else:
print("String does not end with 'World'")
Output:
C:\>python [Link]
String ends with 'World'
The square brackets [] in regular expressions represent a set of characters.
For example,
if we write [ABC], it represents any one character A or B or C. A hyphen (-)
represents a
range of characters. For example, [A-Z] represents any single character from
the range of
capital letters A to Z. Similarly, [a-z] represents any single lowercase letter.
This is useful
to retrieve names from a string. For example, a name like ‘Rahul’ starts with
a capital
letter and remaining are lowercase letters. To search such strings, we can
use a regular
expression as: '[A-Z][a-z]*'. This means the first letter should be any capital
letter (from A
to Z). Then the next letter should be a small letter. Observe the ‘*’, it
represents 0 or more
repetitions of small letters should be considered.

Program 22: A Python program to create a regular expression to retrieve


marks and
names from a given string.
# displaying marks and names
import re;
str = 'Rahul got 75 marks, Vijay got 55 marks, whereas Subbu got 98
marks.'
# extract only marks having 2 digits
marks = [Link]('\d{2}', str)
print(marks)
# extract names starting with a capital letter
# and remaining alphabetic characters
names = [Link]('[A-Z][a-z]*', str)
print(names)
Output:
C:\>python [Link]
['75', '55', '98']
['Rahul', 'Vijay', 'Subbu']
The pipe symbol (|) represents ‘or’. For example, if we write ‘am|pm’, it finds
the strings
which are either ‘am’ or ‘pm’.

Program 23: A Python program to create a regular expression to retrieve


the timings
either ‘am’ or ‘pm’.
import re
str = 'The meeting may be at 8am or 9am or 4pm or 5pm.'
res = [Link](r'\dam|\dpm', str)
print(res)
Output:
C:\>python [Link]
['8am', '9am', '4pm', '5pm']

Using Regular Expressions on Files


We can use regular expressions not only on individual strings, but also on
files where
huge data is available. As we know, a file contains a lot of strings. We can
open the file
and conduct searching or matching etc. operations on the strings of the file
using regular
expressions. For this purpose, first we should open the file as:
f = open('filename', 'r') # open the file for reading
Now, the data of the file is referenced by the file object or file handle ‘f’. We
can read line
by line from the file object using a for loop as:
for line in f:
res = [Link](regexpression, line)
Here, the regexpression will act on each line (or string) of the file and the
result will be
added to res object. Since, the findall() method returns a list with resultant
strings, the
‘res’ object shows a list. Hence, we can check if the list contains any
elements by
checking its size as:
if len(res)>0: # if more than 0 elements are found, then display
print(res)
Let’s assume that a file by the name ‘[Link]’ contains some information
about a project
where mail- ids of team members are mentioned. We can create this file by
opening the
Notepad and type some data as shown in Figure 18.2:

Please observe that the file ‘[Link]’ contains mail-ids of 3 people who are
involved in
the project and we want to retrieve these mail-ids using a regular expression.
A simple
regular expression for this may be in the form of: r'\S+@\S+'. From Table
18.1, we know
that \S represents non-whitespace character. \S+ represents several
characters. A mail
id will have some characters before ‘@’ symbol and after that also. For
example,
nag.r@[Link]. In this mail-id, we can represent ‘nag.r’ with \S+ and
then @ and
after that we can represent ‘[Link]’ with another \S+ character. Now
observe
Program 24 where we are opening the ‘[Link]’ file and reading only the
mail-ids.
Program
Program 24: A Python program to create a regular expression that reads
email-ids from a
text file.
import re
# open the file for reading
f = open('[Link]', 'r')
# repeat for each line of the file
for line in f:
res = [Link](r'\S+@\S+', line)
# display if there are some elements in result
if len(res)>0:
print(res)
# close the file
[Link]()
Output:
C:\>python [Link]
['nag.r@[Link],', 'Mahesh.k@[Link],', 'veena@[Link]']
Let’s take a ‘[Link]’ file that contains employee id number, name, city
and salary as
shown in Figure 18.3:
We want to retrieve employee id numbers and their salaries only from the
‘[Link]’ file
and write that information into another file, say ‘[Link]’. The other
information is not
needed by us. Since employee id numbers are of 4 digits each, we can use a
regular
expression like: r'\d{4}' to retrieve the id numbers. Similarly, salaries are
having 4 or
more digits and a decimal point and then 2 digits. So, to retrieve the salaries,
we can use
regular expression: r'\d{4,}.\d{2}'. Once the id numbers and salaries are
retrieved
using the search() method, we can store them into the ‘[Link]’. This is
shown in
Program 25.

Program 25: A Python program to retrieve data from a file using regular
expressions and
then write that data into a file.
import re
# open the files
f1 = open('[Link]', 'r')
f2 = open('[Link]', 'w')
# repeat for each line of the file f1
for line in f1:
res1 = [Link](r'\d{4}', line) # extract id no from f1
res2 = [Link](r'\d{4,}.\d{2}', line) # extract salary from f1
print([Link](), [Link]()) # display them
[Link]([Link]()+"\t") # write id no into f2
[Link]([Link]()+"\n") # write salary into f2
# close the files
[Link]()
[Link]()
Output:
C:\>python [Link]
1001 15000.00
1002 9000.50
1003 25575.55
1004 19980.75
We can open the ‘[Link]’ file by double clicking it and see the same data
written into
that file.
Retrieving Information from a HTML File
Let’s see how to apply regular expressions on a HTML file and retrieve the
necessary
information. As an example, let’s take a HTML file that contains some items
for breakfast
and their prices in the form of a table, as shown here:
<! [Link]>
<html>
<table border=2>
<tr align="center"><td>1</td> <td>Roti</td> <td>50.00</td></tr>
<tr align="center"><td>2</td> <td>Chapatti</td> <td>55.75</td></tr>
<tr align="center"><td>3</td> <td>Dosa</td> <td>48.00</td></tr>
<tr align="center"><td>4</td> <td>Idly</td> <td>25.00</td></tr>
<tr align="center"><td>5</td> <td>Vada</td> <td>38.90</td></tr>
<tr align="center"><td>6</td> <td>Coffee</td> <td>20.00</td></tr>
<tr align="center"><td>7</td> <td>Tea</td> <td>15.00</td></tr>
</table>
</html>
When we open this file, we can see the table in the browser (See Figure 18.4)
where the
values of item number, item name and price are displayed.

Let’s assume that this file is available in our computer in the directory as:
F:\py\[Link]. To open this file, we have to use urlopen() method of
[Link]
module in Python. So, we have to use the following code:
import [Link]
f = [Link](r'[Link]
Observe the raw string passed to urlopen() method. It contains the path of
the .html file
as:
[Link]
The first word ‘[Link] indicates file URL scheme that is used to refer to files
in the local
computer system. The next word ‘f|py’ indicates the drive name ‘f’ and the
sub directory
‘py’. In this, we have the file [Link]. Once this file is open, we can
read the data
using read() method as:
text = [Link]()
But the data in the HTML files would be stored in the form of byte strings.
Hence, we
have to decode them into normal strings using the decode() method as:
str = [Link]()
Now, we have the string ‘str’. We have to retrieve the required information
from this string
using a regular expression. Suppose we want to retrieve only item name and
price, we
can write:
r'<td>\w+</td>\s<td>(\w+)</td>\s<td>(\d\d.\d\d)</td>'
Please observe that the preceding expression contains three special
characters: the first
one is a \w+, the second one is (\w+) and the third one is (\d\d.\d\d). They
are
embedded in the tags <td> and </td>. So, the information which is in
between the tags is
searched.
The first \w+ indicates that we are searching for a word (item number). The
next \w+ is
written inside parentheses (). The parentheses represents that the result of
the regular
expression written inside these parentheses will be captured. So, (\w+)
stores the words
(item names) into a variable and the next (\d\d.\d\d) stores the words (item
prices) into
another variable. If we use the findall() method to retrieve the information, it
returns a list
that contains these two variables as a tuple in every row. For example, the
first two
values are ‘Roti’ and ’50.00’ which are stored in the list as a tuple as: [('Roti',
'50.00')].

Program 26: A Python program to retrieve information from a HTML file


using a regular
expression.
import re
import [Link]
# open the html file using urlopen() method
f = [Link](r'[Link]
# read data from the file object into text string
text = [Link]()
# convert the byte string into normal string
str = [Link]()
# apply regular expression on the string
result = [Link](r'<td>\w+</td>\s<td>(\w+)</td>\s<td>(\d\d.\d\d)
</td>', str)
# display result
print(result)
# display the items of the result
for item, price in result:
print('Item= %-15s Price= %-10s' %(item, price))
# close the file
[Link]()
Output:
C:\>python [Link]
[('Roti', '50.00'), ('Chapatti', '55.75'), ('Dosa', '48.00'), ('Idly',
'25.00'), ('Vada', '38.90'), ('Coffee', '20.00'), ('Tea', '15.00')]
Item= Roti Price= 50.00
Item= Chapatti Price= 55.75
Item= Dosa Price= 48.00
Item= Idly Price= 25.00
Item= Vada Price= 38.90
Item= Coffee Price= 20.00
Item= Tea Price= 15.00
DATA STRUCTURES IN PYTHON

Data structures store elements in various models. Basically, a data structure


represents arrangement of elements in memory in a particular model.
Data structures are also known as abstract data types (ADTs).

Lists are versatile data structures in Python since they can store any type of
elements
and it is possible to store different types of elements also. Internally, lists are
implemented as arrays in Python. A list is created as an array having
references to
elements or objects. The problem in using arrays as lists is that all the
operations may
not be done in the same amount of time. Inserting and deleting elements at
the end of the
list would be faster than doing the same operations in the beginning or
middle, since the
entire list (i.e. array) elements need to shift towards right. However, except
this minor
inconvenience, lists can be used in our programs to create linked lists, stacks
or queues.
When data is important, we should use file concept. For example employees’
data like
employee id number, name, address, department name, etc. should be
stored permanently
in a file so that it can be retrieved and utilized at any time. Sometimes, logic
will be more
important than data. We may want to store data in memory and apply some
logic to get the
results instantly. Here, we should use data structures. For example, let’s take
customers
queue at the billing counter in a shopping mall. In this case, the information
of the items
purchased by the customers should be entered into computer and calculate
the total bill
amount immediately. In such cases, we need to use queue data structure
since the
customers should be cleared in ‘first in first out’ basis from the queue.
Now, we proceed further to discuss how to implement various fundamental
data
structures with the help of lists in Python.

Linked Lists
A linked list contains a group of elements in the form of nodes. Each node
will have three
fields:
1. The data field that contains data.
2. A link field that contains reference to the previous node.
3. Another link field that contains reference to the next node.
Link fields store references, i.e., memory locations. These link fields are
useful to move
from one node to another node in the linked list so that any operation can be
done in a
minimum amount of time. See Figure 19.1.
Linked list is very convenient to store data. The operations like inserting
elements,
removing elements, searching for an element etc. are done very quickly and
almost in the
same amount of time. Linked list is one of the fastest data structure. Hence,
linked list is
used where time critical operations are to be performed.

Python provides list data type that can be used to implement linked lists. The
following
are the operations that are generally performed on linked lists:
 Traversing the linked list: This means visiting every node and displaying
the data of
the node. Thus all the elements of the node should be displayed. This can be
done
using a for loop on the list elements as:

for element in list:


print(element)
 Appending elements to the linked list: This means adding elements at
the end of
the existing list. This can be done using the append() method of the list as:
append(element)
 Inserting elements into the linked list: This means adding elements in
a particular
position in the existing list. This can be done using insert() method of the list
in the
following format:
insert(position, element)
 Removing elements from the linked list: This is done using remove()
method of the
list as remove(element). If the element being removed is not found, then this
method
raises ValueError.
 Replacing elements in the linked list: Replacing means deleting an
element in a
particular position and inserting a new element in the same position. There is
no
method available in lists in Python to handle this operation. But this can be
achieved
by first using remove() method and then insert() method.
 Searching for an element location in the linked list: This can be done
using
index() method available in lists. index(element) returns the position number
of the
element if exists in the list. If the element does not exist, then it raises
ValueError.
 Size of the linked list: Finding the number of elements in the linked list is
possible
through len() method. len(list) returns an integer that gives the size of the
list.
Program 1 shows how to create a linked list with string type elements. After
creating a
linked list, we are going to perform some operations on the list through a
menu. A menu
represents a group of items or options for the user so that the user can
select any option.
Depending on the user selection, we are supposed to perform the required
operation.
Program
Program 1: A Python program to create a linked list and perform operations
on the list.
# a linked list that stores a group of strings
# create an empty linked list
ll = []
# add some string type elements to ll
[Link]("America")
[Link]("Japan")
[Link]("India")
# display the list
print("The existing list= ", ll)
# display menu
choice=0
while choice<5:
print('LINKED LIST OPERATIONS')
print('1 Add element')

print('3 Replace element')


print('4 Search for element')
print('5 Exit')
choice = int(input('Your choice: '))
# perform a task depending on user choice
if choice==1:
element = input('Enter element: ')
pos = int(input('At what position? '))
[Link](pos, element)
elif choice==2:
try:
element = input('Enter element: ')
[Link](element)
except ValueError:
print('Element not found')
elif choice==3:
element = input('Enter new element: ')
pos = int(input('At what position? '))
[Link](pos)
[Link](pos, element)
elif choice==4:
element = input('Enter element: ')
try:
pos = [Link](element)
print('Element found at position: ', pos)
except ValueError:
print('Element not found')
else:
break
# display the list elements
print('List = ', ll)
Output:
C:\>python [Link]
The existing list= ['America', 'Japan', 'India']
LINKED LIST OPERATIONS
1 Add element
2 Remove element
3 Replace element
4 Search for element
5 Exit
Your choice: 1
Enter element: Russia
At what position? 1
List = ['America', 'Russia', 'Japan', 'India']
LINKED LIST OPERATIONS
1 Add element
2 Remove element
3 Replace element
4 Search for element
5 Exit
Your choice: 3
Enter new element: China
At what position? 2
List = ['America', 'Russia', 'China', 'India']
LINKED LIST OPERATIONS
1 Add element
2 Remove element
3 Replace element
4 Search for element
5 Exit
Your choice: 5

Stacks
A stack represents a group of elements stored in LIFO (Last In First Out)
order. This
means that the element which is stored as a last element into the stack will
be the first
element to be removed from the stack. Inserting elements (objects) into
stack is called
'push operation' and removing elements from stack is called 'pop operation'.
Searching
for an element and returning it without removing it from the stack is called
‘peep
operation’. Insertion and deletion of elements take place only from one side
of the stack,
called ‘top’ of the stack, as shown in Figure 19.2. The other side of the stack
is called
‘bottom’ of the stack which is closed and thus does not allow any operations.

Stacks must strictly follow LIFO order where the last element pushed on to
the top of the
stack should be popped first. Let’s take a hotel where a pile of plates are
made available
to the customers in a counter. These plates are accessible in such a way that
the last
washed plate will be available to the first customer. If the customer takes the
top plate
(the 3rd plate) from the pile, the weight on the spring will be lessened and
the next plate
(2nd one) will come up. See Figure 19.3(a). If the elements are stored in
memory in this
model, then it is called a stack.
Similarly, a Compact Disk holder where the CDs are arranged such that the
last CD is
available first is also an example of a stack. See Figure 19.3(b). If the
elements are
arranged in memory as CDs are in the holder, it is called a stack.

environment. It means that when the program is running, the state of the
variables and
information about the execution status are stored in a stack. Another use of
stack is in
expression evaluation. While evaluating expressions like ax +by * 5, they are
converted
into postfix or prefix notations using a stack and stored into the stack. Later,
they are
retrieved from the stack and evaluated according to certain rules.
Python provides list data types that can be used to create stacks. We should
first create a
Stack class with the following general operations:
 Push operation: It means inserting element at the top of the stack. This
can be done
with the help of append() method of the list as: [Link](element) where
‘st’ is a list.
 Pop operation: It means removing the topmost element from the stack.
This can be
performed using pop() method of the list as: [Link](). This method returns
the
removed element that can be displayed.
 Peep operation: It means returning the topmost element without deleting
it from the
stack. Peep is also known as ‘peek’ operation. This is done by returning st[n-
1]
element where ‘n’ is the number of elements (or size) of the stack. So, if the
stack has
5 elements, the topmost element position will be 4 since the elements are
referred
from 0th to 4th positions.
 Searching operation: It means knowing the position of an element in the
stack from
the top of the stack. For this purpose, the list’s index() method can be used
as:
[Link](element) which returns the position number ‘n’ of the element from
the
beginning (or bottom) of the stack. Once this is known, we can get its
position from
the top of the stack as: size of the stack – n.
 Empty stack or not: This can be judged by simply testing whether the list
‘st’ is
empty or not. We can use an expression as: ‘return st == []’ that returns True
if ‘st’ is
empty else False.
Now, let’s develop Stack class with methods to perform previously
mentioned operations.

Program 2: A Python program to create a Stack class that can perform


some important
operations.
# Stack class – save this as [Link]
class Stack:
def __init__(self):
[Link] = []
def isempty(self):
return [Link] == []
def push(self, element):
[Link](element)
def pop(self):
if [Link]():
return -1
else:
return [Link]()
def peep(self):
n = len([Link])
return [Link][n-1]
def search(self, element):
if [Link]():
return -1
else:
try:
n = [Link](element)
return len([Link])-n
except ValueError:
return -2
def display(self):
return [Link]
Output:
C:\>python [Link]
C:\>
The next step is to use this Stack class in any of our programs. We are using
Stack class
and performing various operations on the stack with the help of a menu in
Program 3.
Since we want to use the Stack class of [Link] program, we have to import
Stack class
from [Link] module as:
from stack import Stack
Program
Program 3: A Python program to perform various operations on a stack
using Stack
class.
# using the Stack class of [Link] program
from stack import Stack
# create empty stack object
s = Stack()
# display menu
choice=0
while choice<5:
print('STACK OPERATIONS')
print('1 Push element')
print('2 Pop element')
print('3 Peep element')
print('4 Search for element')
print('5 Exit')
choice = int(input('Your choice: '))
# perform a task depending on user choice
if choice==1:
element = int(input('Enter element: '))
[Link](element)
elif choice==2:
element = [Link]()
if element == -1:
print('The stack is empty')
else:
print('Popped element= ', element)
elif choice==3:
element = [Link]()
print('Topmost element= ', element)
elif choice==4:
element = int(input('Enter element: '))
pos = [Link](element)
if pos == -1:
print('The stack is empty')
elif pos == -2:
print('Element not found in the stack')
else:
print('Element found at position: ', pos)
else:
break
# display the contents of stack object
print('Stack= ', [Link]())
Output:
C:\>python [Link]
STACK OPERATIONS
1 Push element
2 Pop element
3 Peep element
4 Search for element
5 Exit
Your choice: 1
Enter element: 10
:
:
Stack= [10, 20, 30, 40, 50]
STACK OPERATIONS
1 Push element
2 Pop element
3 Peep element
4 Search for element
5 Exit
Your choice: 2
Popped element= 50
Stack= [10, 20, 30, 40]
STACK OPERATIONS
1 Push element
2 Pop element
3 Peep element
4 Search for element
5 Exit
Your choice: 3
Topmost element= 40
Stack= [10, 20, 30, 40]
STACK OPERATIONS
1 Push element
2 Pop element
3 Peep element
4 Search for element
5 Exit
Your choice: 4
Enter element: 30
Element found at position: 2
Stack= [10, 20, 30, 40]

Queues
We see many queues in our daily life. We see people standing in queues at
railway counters,
bank ATM counters, super market bill counters, cinema ticket counters, etc.
In any queue,
the person who is in the first place will get the service first and then comes
out of the queue.
Any new person will add at the end of the queue. Let’s understand that the
element that first entered the queue will be deleted first from the queue.
This is called FIFO (First In First Out) order. Figure 19.4 shows a queue of
people standing at a bank ATM machine:
In the same way, if we can arrange the elements in memory in such a way
that the first
element will come out first, then that arrangement is called a queue. The
rule is that in
case of a queue, deletion of elements should be done from the front of the
queue and
insertion of elements should be done only at its end. It is not possible to do
any
operations in the middle of the queue. See Figure 19.5:

Queue class that can perform the following general operations:


 Adding a new element: It means inserting element at the rear (or back)
end of the
queue. This can be done using append() method of list as: append(element).
 Deleting an element: It represents removing the element from the front
of the queue.
We can use list’s pop() method to remove the 0th element, as: pop(0).
 Searching the position of an element: This is possible using index()
method
available for lists. Index(element) returns the position number from the front
of the
list. If the element is found at 1st position, the index() method returns 0th
position.
Hence we may have to add 1 to the position number returned by the index()
method.
Now, we are going to develop Queue class in the following program and save
the program
as [Link]. Please see Program 4.
Program
Program 4: A Python program to create a Queue class using list methods.
# Queue class – Save this as [Link]
class Queue:
def __init__(self):
[Link] = []
def isempty(self):
return [Link] == []
def add(self, element):
[Link](element)
def delete(self):
if [Link]():
return -1
else:
return [Link](0)
def search(self, element):
if [Link]():
return -1
else:
try:
n = [Link](element)
return n+1
except ValueError:
return -2
def display(self):
return [Link]
It is possible to use this Queue class of [Link] module in any Python
program. In
Program 5, we create a queue with float values and then perform some
important
operations on the queue through a menu.
Program
Program 5: A Python program to perform some operations on a queue.
# using the Queue class of [Link] program
from que1 import Queue
# create empty queue object
q = Queue()
# display menu
choice=0
while choice<4:
print('QUEUE OPERATIONS')
print('1 Add element')
print('2 Delete element')
print('3 Search for element')
print('4 Exit')
choice = int(input('Your choice: '))
# perform a task depending on user choice
if choice==1:
element = float(input('Enter element: '))
[Link](element)
elif choice==2:
element = [Link]()
if element == -1:
print('The queue is empty')
else:
print('Removed element= ', element)
elif choice==3:
element = float(input('Enter element: '))
pos = [Link](element)
if pos == -1:
print('The queue is empty')
elif pos == -2:
print('Element not found in the queue')
else:
print('Element found at position: ', pos)
else:
break
# display the contents of queue object
print('Queue= ', [Link]())
Output:
C:\>python [Link]
QUEUE OPERATIONS
1 Add element
2 Delete element
3 Search for element
4 Exit
Your choice: 1
Enter element: 10.5
:
:
Queue= [10.5, 22.5, 30.0, 45.0, 50.75]
QUEUE OPERATIONS
1 Add element
2 Delete element
3 Search for element
4 Exit
Your choice: 3
Enter element: 22.5
Element found at position: 2
Queue= [10.5, 22.5, 30.0, 45.0, 50.75]
QUEUE OPERATIONS
1 Add element
2 Delete element
3 Search for element
4 Exit
Your choice: 2
Removed element= 10.5
Queue= [22.5, 30.0, 45.0, 50.75]
QUEUE OPERATIONS
1 Add element
2 Delete element
3 Search for element
4 Exit
Your choice: 4

Deques
Every time when we store an element at the end of a queue, internally the
size of the
queue should be increased. Similarly, when we delete an element at the
front, all the
elements should be shifted towards front (i.e. left), otherwise there will be
empty memory
blocks created at the beginning of the queue that may not be reusable. Such
troubles can
be eliminated if we can allow the insertions and deletions at both ends of the
queue. This
type of queue is called double-ended queue or simply ‘deque’. See Figure
19.6:

remove the elements from the front and from the rear side (end) also. So,
deques are
more flexible and can be imagined as generalized form of queues and stacks
that we
discussed in the previous sections. For example, if we insert the delete the
elements only
from one end of the deque, then it becomes a stack. If we insert the
elements from one
end and delete the elements from the other end, then it becomes a queue.
In Python, a class by the name ‘deque’ is provided in ‘collections’ module to
work with
deques. Python people used arrays internally in implementing lists which
form the basis
for creating queues. But in case of deques, they used doubly linked lists to
implement the
deques. Hence, deques are faster than the normal queues. To create a
deque, we should
simply create an object to deque class as:
d = deque()
In this case, the deque can accommodate arbitrary number of elements. We
can restrict
the size of the deque by specifying ‘maxlen’ attribute as:
d = deque(maxlen= 100)
In the preceding statemnet, we are creating a deque which can
accommodate a maximum
of 100 elements only. The following are the operations provided in deque
class in Python:
 Adding element at the front: Assuming that the front will be at the left
side of the
deque, we can add an element at the front using appendleft() method, as:
appendleft(element).
 Deleting element at the front: This can be done using popleft() method,
as: element
= popleft(). This method returns a copy of the element that is deleted.
 Adding element at the rear: This can be done using append() method
that adds the
element at the right side of the deque, i.e. at the rear. The way we can use
the
append() method is: append(element).
 Deleting element at the rear: This can be achieved by calling pop()
method on the
deque. This method when called without any argument will remove the
element at the
rear of the deque.
 Deleting element at any place: This can be done using remove()
method. If we use
remove(element), it will remove the element from the rear part of the deque.
This
method raises ValueError if the element being removed is not found.

 Searching for an element in the deque: We can find out how many
times an
element has occurred in the deque with the help of count() method.
count(element)
will return the number of times the element is found. If the element is not
found in
the deque, then this method returns 0.
 Reversing the deque: We can use reverse() method to reverse the order
of the
elements in the deque. This method is used as: reverse() and it returns None.
In Program 6, we are creating a deque that can store characters or strings.
Also, we are
using a menu to perform some important operations on the deque.
Program
Program 6: A Python program to create and use deque.
# deque operations
from collections import deque
# create an emtpy deque
d = deque()
choice=0
while choice<7:
print('DEQUE OPERATIONS')
print('1 Add element at front')
print('2 Remove element at front')
print('3 Add element at rear')
print('4 Remove element at rear')
print('5 Remove element in the middle')
print('6 Search for element')
print('7 Exit')
choice = int(input('Your choice: '))
# perform a task depending on user choice
if choice==1:
element = input('Enter element: ')
[Link](element)
elif choice==2:
if len(d) == 0:
print('Deque is empty')
else:
[Link]()
elif choice==3:
element = input('Enter element: ')
[Link](element)
elif choice==4:
if len(d) == 0:
print('Deque is empty')
else:
[Link]()
elif choice==5:
element = input('Enter element: ')
try:
[Link](element)

except ValueError:
print('Element not found')
elif choice==6:
element = input('Enter element: ')
c = [Link](element)
print('No of times the element found: ', c)
else:
break
# display the deque elements using for loop
print('Deque= ', end='')
for i in d:
print(i, ' ', end='')
print() # move cursor to next line
Output:
C:\>python [Link]
DEQUE OPERATIONS
1 Add element at front
2 Remove element at front
3 Add element at rear
4 Remove element at rear
5 Remove element in the middle
6 Search for element
7 Exit
Your choice: 1
Enter element: A
Deque= A
DEQUE OPERATIONS
1 Add element at front
2 Remove element at front
3 Add element at rear
4 Remove element at rear
5 Remove element in the middle
6 Search for element
7 Exit
Your choice: 1
Enter element: B
Deque= B A
DEQUE OPERATIONS
1 Add element at front
2 Remove element at front
3 Add element at rear
4 Remove element at rear
5 Remove element in the middle
6 Search for element
7 Exit
Your choice: 3
Enter element: C
:
:
Deque= B A C D E F
DEQUE OPERATIONS
1 Add element at front
2 Remove element at front
3 Add element at rear
4 Remove element at rear
5 Remove element in the middle
6 Search for element

Your choice: 2
Deque= A C D E F
DEQUE OPERATIONS
1 Add element at front
2 Remove element at front
3 Add element at rear
4 Remove element at rear
5 Remove element in the middle
6 Search for element
7 Exit
Your choice: 6
Enter element: E
No of times the element found: 1
Deque= A C D E F

You might also like