Python Notes 2
Python Notes 2
In Python, strings, lists, tuples and dictionaries are very important sequence
datatypes. All sequences allow some common operations like indexing and
slicing.
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:
print(student[1])
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])
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.
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: []
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)
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])
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: '))]
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:
Output:
C:\>python [Link]
(10, 20, 30, 40, 50)
Enter position no: 3
(10, 20, 40, 50)
DICTIONARIES
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.
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.
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.
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’.
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
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}
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.
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 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.
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
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.
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.
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.
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].
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’.
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.
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]().
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 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
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
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.
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
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.
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.
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.
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:
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
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.
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.
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.
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.
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.
Output:
C:\>python [Link]
Square value= 256
Square root= 4.0
Cube value= 4096
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:
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:
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.
C:\>python [Link]
Enter database name: Sybase
Connecting to Sybase database...
Disconnected from Sybase.
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
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.
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.
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.
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.
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.
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.
1. To catch the exception which is raised in the try block, we can write except
block with
the Exceptionclass name as:
except Exceptionclass:
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.
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
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
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:
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:
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]’.
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
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
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.
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.
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.
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]*'.
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.
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.
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.
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')].
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:
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.
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:
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