Python Programming Made Easy (2016)
Chapter 9: Classes ,Objects and Inheritance
A class is a definition of an object. A class is a user defined data type just like int, and an object of this class is just a variable. When we define a class, we define a blueprint for a data type. A class is used to specify the form of an object and it combines data representation and methods for manipulating that data into one neat package. The data and functions within a class are called members of the class.
Namespaces
A namespace is a mapping from names to objects. It is a place where a variable’s name is stored. Python has the following namespaces and searches in the following order.
· Local can be inside a function or class method, for example inside def
· Enclosed can be its enclosing function, e.g., if a function is wrapped inside another function. The variable is searched in all enclosing functions from inner to outer.
· Global refers to the uppermost level. The variable is searched in global modules or for names declared global in a def.
· Built-in are special names that Python reserves for itself.
Fig 9.1: Python Namespaces
Defining classes
A class definition starts with the keyword class followed by the class name and a colon; For example we defined the Student data type using the keyword class as follows:
class Student: count =0 # constructor to create an object def __init__(self,rollno,marks): self.rollno = rollno self.marks = marks def read (self): # This function gets the details of a student from the user Student.count+=1 def display (self): # This function prints each student's details |
In the above class definition rollno,marks,read() and display() are attributes of the class student.
The init function is the first method and is a special method called constructor or initialization method. It is used to initialize the data members of a class. It is the first piece of code executed in a newly created instance of the class.
It can take any number of arguments and also default arguments.
Objects
An object is created from a class. We declare objects of a class with exactly the same sort of declaration that we declare variables of basic types. Following statements declare two objects of class Student:
S1 =Student(); // Declare s1 of type Student S2= Student(); // Declare s2 of type Student |
Both of the objects s1 and s2 will have their own copy of data members.
Objects of a class have a same structure and behavior. They will only differ regarding their state (values).
Self
The first argument of every class method is always a reference to the current instance of the class named “self”. This is the only difference between a class method and an ordinary function. “self” is not a keyword and is just used for readability and uniformity. It is an instance identificator.
Here is an example using self
class Stock: discount=5 # constructor to create an object def __init__(self,icode,price,qty): self.icode = icode self.price = price self.qty = qty # Displays item details def display (self): print "Item Code:",self.icode print "Price: Rs",self.price print "Quantity: ",self.qty |
In the above example, discount is a class variable whose value would be shared among all instances of this class. They can be accessed as
Stock.discount
Instance attributes belong to each instance/object of a class. i.e. S1.icode and s2.icode will have 2 different values
ACCESSING ATTRIBUTES
Attributes can be accessed by using the dot operator.
· The getattr(obj, name[, default]) : to access the attribute of object.
· The hasattr(obj,name) : to check if an attribute exists or not.
· The setattr(obj,name,value) : to set an attribute. If attribute does not exist, then it would be created.
· The delattr(obj, name) : to delete an attribute.
getattr(obj1, ‘price') # Returns value of 'price' attribute hasattr(obj1, ‘price’) # Returns true if 'price' attribute exists setattr(obj1, 'price', 30.56) # Set attribute 'price' at 30.56 delattr(obj1, 'price') # Delete attribute 'price' |
BUILT-IN CLASS ATTRIBUTES
Every Python class keeps following built-in attributes and they can be accessed using dot operator like any other attribute:
· __dict__ : Dictionary containing the class's namespace.
· __doc__ : Class documentation string or None if undefined.
· __name__: Class name.
· __module__: Module name in which the class is defined. This attribute is "__main__" in interactive mode.
· __bases__ : A possibly empty tuple containing the base classes, in the order of their occurrence in the base class list.
__ del()__ or destructor
This function is called when the instance is about to be destroyed. It calls the method - object.__del__(self)
Consider the following command is given
>>>del S1
The above command doesn't directly call S1.__del__(). First the reference count for S1 is decremented by one and __del()__ is called only when S1's reference count reaches zero i.e. when all the variables referenced by S1 have been deleted.
__ str()__
It is a special function which returns the string representation of the objects. It calls the method
object.__str__(self). If the class defines a __str__ method, Python will call it when you call the str() or use print statement. The str() built-in function, when used along with the print statement computes the "informal" string representation of an object.
class Student: ………. ……….. def __str__(self): return "This class is about a Student?" >>> S=Student() >>> print S Hello, How are you? |
When you give the command to "print S", Python calls str(S) to get the string representation of S. If the class of S has a __str__ method, str(S) becomes a call to S.__str__(). This returns the string to print.
Private variables
“Private” instance variables are not supported in Python. It can be achieved through name mangling. A name is prefixed with two leading underscores and no more than one trailing underscore to make it as a private member.
Class Student:
Rollno = 121
Grade = ‘A’
__age = 15 # private member
Data hiding is a means to enforce encapsulation. To access this hidden variable, we need to follow the syntax
ClassName.__var-name
Ex:- Student.__age=20
DYMANICALLY ADDING METHODS
Dynamic Class Method Addition
Occasionally you may need dynamically add a function as a method to a class. This is easily accomplished by assigning the function as an attribute of the class.
def fn(self):
return id(self), self, type(self)
class A_Class(object):
def method_a(self):
return id(self), self, type(self)
A = A_Class()
# Modify the class and add fn as a method
setattr(A_Class, 'method_b', fn)
A.method_a()
# Call the dynamically added method
A.method_b()
Dynamic Instance Method Addition
When we add the method to a class all instances can access it. If we only want a particular instance to have a method do this:
from types import MethodType
A = A_Class()
setattr(A, fn.__name__, MethodType(fn, A, type(A)))
# Calls the fn method attached to the instance A
A.fn()
# Throws an exception
A.fn()
Static Methods
A static method is callable without an object.
class Student:
@staticmethod
def getStudentcount():
return count
print Student.getStudentCount()
In the above example, the function getStudentcount () dosen’t use self. They are preceded with a decorator @staticmethod and it is being called without the instance. It eases the readability of the code. Static methods are accessible from either an object or a class.
Destroying Objects (Garbage Collection)
Python automatically allocates and de-allocates memory. The user does not have to preallocate or deallocate memory by hand as one has to when using dynamic memory allocation in languages such as C or C++. Python uses two strategies for memory allocation-reference counting and automatic garbage collection.
i) Reference Counting
Reference counting counts the number of times an object is referenced by other objects in the system. An object's reference count changes as the number of aliases that point to it change. An object's reference count increases when it is assigned a new name or placed in a container (list, tuple or dictionary). The object's reference count decreases when it is deleted with del, its reference is reassigned, or its reference goes out of scope. When an object's reference count reaches zero, Python collects it automatically.
Consider the code given below:
A=5 # an object A is created which is bound to 5.
B=A # increase in reference count of 5
C[0]={B} # increase in reference count of 5
del A # decrease in reference count of 5
B=10 # decrease in reference count of 5
A class can implement the special method __del__(), called a destructor, that is invoked when the instance is about to be destroyed. Reference counting is extremely efficient but it cannot handle reference cycles. A reference cycle is when there is no way to reach an object but its reference count is still greater than zero. The easiest way to create a reference cycle is to create an object which refers to itself as in the example below:
def reference_cycle():
x=[ ]
x.append(x)
reference_cycle()
In the above example since reference_cycle( ) creates an object x which refers to itself (statement x.append(x)), x will not automatically be freed when the function returns. This will cause the memory that x is using to be held onto until the Python garbage collector is invoked.
ii) Automatic Garbage Collection
In this case garbage collection is a scheduled activity. Python schedules garbage collection based upon a threshold of object allocations and object de-allocations. Python deletes the objects which are not required, may it be built-in types or class instances, through the process named garbage collection.
When the number of allocations minus the number of de-allocations are greater than the threshold number, the garbage collector is run and the unused block of memory is reclaimed. One can inspect the threshold for new objects by loading the gc module and asking for garbage collection thresholds.
Automatic garbage collection will not run if your Python device is running out of memory. In such case, your application will throw exceptions, which must be handled otherwise your application will crash. It occurs more for the number of free objects not the size of objects.
Inheritance
Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application. This also provides an opportunity to reuse the code functionality and fast implementation time.
When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class, and the new class is referred to as the derived class. The idea of inheritance implements the IS-A relationship. For example, mammal IS-A animal, dog IS-A mammal hence dog IS-A animal as well and so on.
Forms of inheritance
Single inheritance: When a subclass inherits from only one base class, and there are only 2 classes in this relationship, then it is known as single inheritance.
Fig 10.1: Single Inheritance
Multilevel inheritance: When a subclass inherits from a class that itself inherits from another class, it is known as multilevel inheritance.
Fig 10.2: Multilevel Inheritance
Multiple inheritance: When a subclass inherits from multiple base classes it is known as multiple inheritance.
Fig 10.3: Multiple Inheritance
Hierarchical inheritance: When many subclasses inherit from a single base class it is known as hierarchical inheritance.
Fig 10.4: Hierarchical Inheritance
Hybrid inheritance: Hybrid inheritance combines 2 or more forms of inheritance.
Fig 10.5: Hybrid Inheritance
Base Overloading Methods
S.No |
Method |
Description |
Usage |
1 |
__init__ ( self [,args...] ) |
Constructor |
obj = className(args) |
2 |
__del__( self ) |
Destructor, deletes an object |
del obj |
3 |
__repr__( self ) |
Evaluateable string representation |
repr(obj) |
4 |
__str__( self ) |
Printable string representation |
str(obj) |
5 |
__cmp__ ( self, x ) |
Comparison of objects |
cmp(obj, x) |
Single Inheritance
Person is the base class from which a student derives. It has 2 private data members: person’s name as string and age as an integer. It has 2 methods read() and disp() to get and display person details.
Student class is defined with roll number as an integer and average as float. It has 3 member functions
Ø readStudent() – internally calls read() of Person class and gets the roll no and average of student.
Ø disprollno() – displays roll number of student
Ø getaverage() – returns the average of the student
Fig 10.6: Single Inheritance example
class Person(object): # constructor to create an object def __init__(self): self.name = "" self.age = 0 def get(self): self.name = raw_input("Enter the Name:") self.age = int(raw_input("Age?")) def disp(self): print "Name :",self.name,"\t Age :",self.age class Student(Person): # constructor to create an object def __init__(self): super(Student, self).__init__() self.rollno = 0 self.avg = 0.0 def readStudent(self): # Call get method of parent class super(Student,self).get() self.rollno = int(raw_input("Enter the Roll Number:")) self.avg = float(raw_input("Average?")) def disprollno(self): print "Roll No. :",self.rollno def getaverage(self): return self.avg |
The class Student extends the class Person. The class student adds 2 new instance variables rollno and avg. In order to add new instance variables, the __init__() method defined in class Person needs to be extended. This function of subclass Student initializes the name and age attributes of the superclass Person and also creates new attributes rollno and avg.
Let’s execute the following statements
p = Person() p.get() s = Student() s.readStudent() |
It would produce the following output.
In the above code segment, the object (p) of class person takes values for instance variables name and age. The object (s) of class student can invoke readstudent() function.
The init function can be extended in in 2 ways.
(i) By using super() function: The init() method is defined in the base class and is extended in the sub class. Super() function is used to call base class methods which has been extended in derived class.
Ex:- super(Student,self).__init__(self,name,age)
(ii) By using super class’s name: The name of the base class can also be used to access the method of the base class which has been extended.
Ex:- Person.__init__(self,name,age)
MULTILEVEL INHERITANCE
The above single inheritance can be extended to a multilevel inheritance. GradStudent is another class which derives from the class Student. So GradStudent indirectly inherits from Person class. It has 2 members subject of graduation and workstatus as a single character y-yes and n-no. It has 3 member functions
Ø readGrad() – calls readStudent() to get student details (which in turn calls read() of Person class) and also gets the Graduate’s details
Ø workstatus() – returns the workstatus y or n
Ø displaySubject() – prints the subject
class GradStudent(Student): def __init__(self): super(GradStudent, self).__init__() self.subject = "" self.working = '' def readGrad (self): # Call readStudent method of parent class super(GradStudent, self).readStudent() self.subject = raw_input("Enter subject of graduation") self.working = raw_input("Working? y/n") def workstatus(self): return self.working def displaysubject (self): print "Subject:",self.subject |
The same way init() and the other methods can be called using super or the class name.
Multiple Inheritance
Python supports multiple inheritance. The general syntax of multiple inheritance is given below.
class Derived(Base1, Base2, Base3):
<statement1>
……
<statement N>
If an attribute/method is not found in Derived, it is searched in Base1 and only if it is not there it is searched in the subsequent base classes.
Method Overriding
Overriding enables the programmer to provide specific implementation to a method in the derived class. So, the method invoked depends on the object used to invoke it. If base class object is used then base class version of the method is called else the derived class method is called.
· The issubclass(sub, sup) boolean function returns true if the given subclass sub is indeed a subclass of the superclass sup.
· The isinstance(obj, Class) boolean function returns true if obj is an instance of class Class or is an instance of a subclass of Class
Abstract methods
An unimplemented method is called an abstract method. These methods are declared in a parent class, but not implemented in it. The implementation is done in the derived class.
class Shape(object): def findArea(self): raise NotImplementedError S = Shape() S.findArea() |
We get the following error
Now, the above class can be extended as below
class Shape(object): def findArea(self): raise NotImplementedError class Square(Shape): def __init__(self,side): self.side =side def findArea(self): return self.side * self.side S = Square(10) print S.findArea() |
The output of the program is shown below
When an abstract method is declared in a base class, the derived class has to either define the method or raise “NotImplementedError”.
The error will be raised only if we try to use that method. This might be useful when we do not want to implement certain methods in the derived class. But if we need to ensure that the derived class overrides this method we could use the “abc” module of Python.
import abc class Shape(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def findArea(self): """Method that should do something.""" class Square(Shape): def __init__(self,side): self.side =side def findArea(self): return self.side * self.side S1 = Shape() S2 = Square(10) |
Here the object S2 doesn’t create a problem because the findArea() method is implemented.
Solved Questions
1. What do you understand about a member function? How does it differ from an ordinary function?
Ans: A member function is part of a class and can be called only with an object. An ordinary function can’t be called by an object. A member function takes “self” as the first argument while an ordinary function doesn’t.
2. Predict the output of the following code snippet
ptr=40
def result():
print ptr
ptr=90
def func(var):
if var<=60:
ptr=30
print ptr
result()
func(60)
func(70)
Ans:
UnboundLocalError: local variable 'ptr' referenced before assignment
3. Predict the output of the following code snippet
ptr=50
def result():
global ptr
ptr=ptr+1
print ptr
result()
print ptr
Ans.
51
51
4. Name the methods that can be used to
a. access attribute of an object
b. delete an attribute of an object
Ans.
a. getattr(obj, name[, default])
b. delattr(obj, name)
5. Give the statement to
a. Check whether the attribute str exists in the class Test whose object is T1
b. Assign a value "Hello" to the attribute str of class Test and object T1.
Ans.
a. hasattr (T1,str)
b. setattr (T1, str, “Hello”)
6. Write a Python program using classes and objects to simulate result preparation system for 20 students. The data available for each student includes: Name, Rollno, and Marks in 3 subjects. The percentage marks and grade are to be calculated from the following information
Percentage of marks |
Grade |
80 to 100 |
A |
60 to 80 |
B |
45 to 60 |
C |
Less than 45 |
D |
Grade formula
Also demonstrate constructor overloading.
Ans.
class Student:
# constructor to create an object
def __init__(self,s=None):
#non-copy constructor
if s==None:
self.rollno = 0
self.name = ''
self.marks = [0,0,0]
self.avg = 0.0
self.grade = ''
# copy constructor
else:
self.rollno = s.rollno
self.name = s.name
self.marks = s.marks
self.avg = s.avg
self.grade = s.grade
def read (self):
# This function gets the details of a student from the user
self.rollno = raw_input("Enter roll number:-")
self.name = raw_input("Enter name:-")
s=0
for i in range(0,3):
self.marks[i] = int(raw_input("Enter the marks ?"))
s = s + self.marks[i]
self.avg = s/3
if(self.avg>60):
self.grade='A'
elif self.avg>40:
self.grade='B'
else:
self.grade='C'
def display (self):
# This function prints each student's details
print self.rollno,"\t",self.name,"\t\t",self.grade
s = Student()
studlist = []
num = int(raw_input("Enter no. of students:-"))
for i in range(0,num):
s.read()
studlist.append(Student(s))
print " STUDENT REPORT \n"
print "*******************************************\n"
print "RollNo \t Name \t\t Grade"
print "*******************************************\n"
for i in range(0,num):
studlist[i].display()
#Initialize avg as the first student's avg
maxavg = studlist[0].avg
totavg = 0
for i in range(1,num):
totavg = totavg + studlist[i].avg
if studlist[i].avg > maxavg:
maxavg = studlist[i].avg
topper = studlist[i].name
totavg = totavg/num
print "Class topper is",studlist[i].name,"with average",studlist[i].avg
print "Class average is",totavg
7. Define a class Travel in Python with the following descriptions :
Private Members:
PlanCode of type long
Place of type character array (string)
No_of_travellers of type integer
No_of_buses of type integer
Public Members:
A constructor to assign initial values of Plan Code as 1001, Place as “Agra”,
No_of_travellers as 5, No_of_buses as 1
A function NewPlan( ) which allows user to enter PlanCode, Place and
No_of_travellers. Also, assign the value of No_of_buses as per the
following conditions :
No_of_travellers No_of_buses
Less than 20 1
Equal to or more than 20 and less than 40 2
Equal to 40 or more than 40 3
A function ShowPlan( ) to display the content of all the data members on screen.
Ans:
class Travel:
def __init__(self):
self.PlanCode=1001
self.Place="Agra"
self.No_of_travellers=5;
self. No _of_buses=1;
def NewPlan(self):
self.PlanCode=input("Enter plan code")
self.Place=raw_input("Enter Place")
self. No_of_travellers=input("Enter No. of travellers")
if(self. No_of_travellers<20):
self. No _of_buses=1;
elif (self. No_of_travellers<40):
self. No_of_buses=2;
else:
self. No _of_buses=3;
def ShowPlan(self):
print self.PlanCode,self.Place,self.No_of_travellers,self.No_of_buses
t=Travel()
t.NewPlan()
t.ShowPlan()
8. Define the term inheritance.
Ans. Inheritance is a mechanism in which a new class is derived from an already defined class. The derived class is known as a subclass or a child class. The pre-existing class is known as base class or a parent class or a super class. The mechanism of inheritance gives rise to hierarchy in classes. The major purpose of inheriting a base class into one or more derived class is code reuse. The subclass inherits all the methods and properties of the super class.
9. Give one example for abstract method?
Ans. An abstract method is a method declared in a parent class, but not implemented in it. The implementation of such a method can be given in the derived class.
class circle(object):
def get_radius(self):
10. What is single inheritance?
Ans. : In single inheritance a subclass is derived from a single base class.
11. What is multiple inheritance? Explain with an example.
Ans. : In this type of inheritance, the derived class inherits from one or more base classes. In the figure below, X and Y are the base classes while Z is the derived class.
12. Give one example of multilevel inheritance.
Ans. : In multilevel inheritance, the derived class becomes the base of another class.
Ex: Country is the base class of State and City is the derived class of State.
13. Define a class employee in Python with the given specifications:
Instance variables:
Employee number, name
Methods:
Getdata()- To input employee number and name
Printdata()- To display employee number and name
Define another class payroll, which is derived from employee
Instance variable
Salaryomputer Science
Methods:
Inputdata() - To call Getdata() and input salary.
Outdata() - To call printdata() and to display salary.
Define another class leave, which is derived from payroll.
Instance variable
No of days
Methods:
acceptdata() - To call Inputdata() and input no of days.
showdata() - To call Outdata() and to display no of days.
Implement the above program in python.
Ans.
class Employee(object):
# constructor to create an object
def __init__(self):
self.eno=0
self.name=''
def Getdata(self):
self.eno=input("Enter the Employee no:")
self.name= raw_input("Enter the Employee Name:")
def Printdata(self):
print "Employee Number",self.eno
print "Employee Name",self.name
class payroll(Employee):
def __init__(self):
super(payroll,self).__init__()
self.salary=0
def Inputdata(self):
self.Getdata()
self.salary=float(raw_input("Enter the salary:"))
def Outdata(self):
self.Printdata()
print "Salary is",self.salary
class leave(payroll):
def __init__(self):
super(leave,self).__init__()
self.Noofdays=0
def acceptdata(self):
self.Inputdata()
self.Noofdays =input("Enter leave days")
def showdata(self):
self.Outdata()
print "No. of leave days",self.Noofdays
leaveobj = leave()
leaveobj.acceptdata()
leaveobj.showdata()
Practice Questions
1. How are objects destroyed in Python?
2. Can we add methods dynamically in Python? Explain with an example.
3. Explain the importance of self in Python classes.
4. Define a class ITEMINFO in Python with the following description:
ICode (Item Code), Item (Item Name), Price (Price of each item), Qty (quantity in stock), Discount (Discount percentage on the item), Netprice (Final Price)
Methods
A member function FindDisc( ) to calculate discount as per the following rule:
If Qty<=10 Discount is 0
If Qty (11 to 20) Discount is 15
If Qty>=20 Discount is 20
A constructor( __init__ method) to assign the value with 0 for ICode, Price, Qty, Netprice and Discount and null for Item respectively
A function Buy( ) to allow user to enter values for ICode, Item, Price, Qty and call function FindDisc( )to calculate the discount and Netprice(Price*Qty-Discount).
A Function ShowAll( ) to allow user to view the content of all the data members
5. Define a class Bank in Python with the following description: Data members: Name of the depositor: String, Account number:long, Type of account (Savings or current):character, Balance amount:decimal.
Member Functions:
Initialize () – It reads the customer name, account number; account type and current balance and initializes the customer object.
Deposit () – Increases balance amount.
Withdraw () – Checks for minimum balance in current account and decreases the balance accordingly.
Display () – Displays the customer details.
6. What is the use of the init() function in inheritance? Explain the 2 ways in which it could be invoked.
7. What is the significance of super( ) function?
8. What are abstract methods? How do we implement abstract methods in Python. Give an example.
9. Find the output of the following code and write the type of inheritance:
class person(object):
def __init__(self,name):
self.name=name
def display(self):
print "Name :",self.name
class student(person):
def __init__(self,name,rollno,marks):
super(student,self).__init__(name)
self.rollno=rollno
self.marks=marks
def disp(self):
self.display()
print " Roll No:",self.rollno
print " Marks :",self.marks
s=student(‘Kali’,11009,78)
s.disp()
10. Define a class furniture in Python with the given specifications:
Instance variables: Type,Model,
Methods: getType() - To return instance variable Type
getModel()- To return instance variable Model
show() To print instance variable Type and Model
Define a class sofa:
No_of_seats ,Cost
Methods
getSeats() To return instance variable No_of_seats
getCost() To return instance variable Cost
show() To print instance variable No_of_seats and Cost
This class inherits class furniture.