4.1 Classes and Objects: Python Application Programming (15CS664)
4.1 Classes and Objects: Python Application Programming (15CS664)
MODULE IV
Page 1
Python Application Programming (15CS664) Module IV
MODULE IV
→ Programmer-defined Types
• A class in Python can be created using a keyword class.
• Here, we are creating an empty class without any members by just using the keyword passwithin it.
class Point:
pass
print(Point)
• The term main indicates that the class Point is in the main scope of the current module.
• In other words, this class is at the top level while executing the program.
• Now, a user-defined data type Point got created, and this can be used to create any number of
objects of this class.
• Observe the following statements:
p=Point()
• Now, a reference (for easy understanding, treat reference as a pointer) to Point object is created
and is returned. This returned reference is assigned to the object p.
• The process of creating a new object is called as instantiation and the object is instance of a
class.
• When we print an object, Python tells which class it belongs to and where it is stored in the
memory.
print(p)
Page 2
Python Application Programming (15CS664) Module IV
• The output displays the address (in hexadecimal format) of the object in the memory.
• It is now clear that, the object occupies the physical space, whereas the class does not.
→ Attributes
• An object can contain named elements known as attributes.
• One can assign values to these attributes using dot operator.
• For example, keeping coordinate points in mind, we can assign two attributes x and y for the
object of a class Point as below
p.x =10.0
p.y =20.0
• A state diagram that shows an object and its attributes is called as object diagram.
• For the object p, the object diagram is shown in Figure below.
Point
x 10.0
p 20.0
y
• The diagram indicates that a variable (i.e. object) p refers to a Point object, which contains two
attributes.
• Each attributes refers to a floating point number.
• One can access attributes of an object as shown –
>>> print(p.x)
10.0
>>> print(p.y)
20.0
• Here, p.x means “Go to the object p refers to and get the value of x”.
• Attributes of an object can be assigned to other variables
>>> x= p.x
>>> print(x)
10.0
Page 2
Python Application Programming (15CS664) Module IV
➢ A function distance() which takes two objects of Point class as arguments and computes the
Euclidean distance between them.
➢ A function print_point()to display one point in the form of ordered-pair.
Program:
import math
class Point:
""" This is a class Point representing a coordinate point"""
def read_point(p):
p.x=float(input("x coordinate:"))
p.y=float(input("y coordinate:"))
def print_point(p):
print("(%g,%g)"%(p.x, p.y))
def distance(p1,p2):
d=math.sqrt((p1.x-p2.x)**2+(p1.y-p2.y)**2)
return d
Page 3
Python Application Programming (15CS664) Module IV
Note that, you need to type two underscores, then the word doc and again two underscores.In
the above program, there is no need of docstring and we would have just used pass to
indicate an empty class. But, it is better to understand the professional way of writing user-
defined types and hence, introduced docstring.
• The function read_point() take one argument of type Point object. When we use the
statements like,
read_point(p1)
the parameter p of this function will act as an alias for the argument p1. Hence, the
modification done to the alias p reflects the original argument p1. With the help of this
function, we are instructing Python that the object p1 has two attributes x and y.
• The function print_point() also takes one argument and with the help of format- strings, we
are printing the attributes x and y of the Point object as an ordered-pair (x,y).
• As we know, the Euclidean distance between two points (x1,y1) and (x2,y2) is
(x1 − x2)2 + ( y1 − y2 )2
In this program, we have Point objects as (p1.x, p1.y) and (p2.x, p2.y). Apply the formula on
these points by passing objects p1 and p2 as parameters to the function distance(). And then
return the result.
Thus, the above program gives an idea of defining a class, instantiating objects, creating attributes,
defining functions that takes objects as arguments and finally, calling (or invoking) such functions
whenever and wherever necessary.
Page 4
Python Application Programming (15CS664) Module IV
NOTE: User-defined classes in Python have two types of attributes viz. class attributes and instance
attributes. Class attributes are defined inside the class (usually, immediately after class header). They
are common to all the objects of that class. That is, they are shared by all the objects created from that
class. But, instance attributes defined for individual objects. They are available only for that instance
(or object). Attributes of one instance are not available for another instance of the same class.
For example, consider the class Point as discussed earlier –
class Point:
pass
This clearly indicates that the attributes x and y created are available only for the object p1, but not
for p2. Thus, x and y are instance attributes but not class attributes.
We will discuss class attributes late in-detail. But, for the understanding purpose, observe the
following example –
class Point:
x=2
y=3
Here, the attributes x and y are defined inside the definition of the class Point itself. Hence, they are
available to all the objects of that class.
→ Rectangles
• It is possible to make an object of one class as an attribute to other class.
• To illustrate this, consider an example of creating a class called as Rectangle.
• A rectangle can be created using any of the following data –
➢ By knowing width and height of a rectangle and one corner point (ideally, a bottom- left
corner) in a coordinate system
➢ By knowing two opposite corner points
➢ Let us consider the first technique and implement the task: Write a class Rectangle containing
Page 5
Python Application Programming (15CS664) Module IV
class Point:
""" This is a class Point representing coordinate point"""
class Rectangle:
""" This is a class Rectangle. Attributes: width, height and Corner Point """
def find_center(rect):
p=Point()
p.x = rect.corner.x + rect.width/2
p.y = rect.corner.y + rect.height/2
return p
def print_point(p):
print("(%g,%g)"%(p.x, p.y))
center=find_center(box)
print("The center of rectangle is:")
print_point(center)
resize(box,50,70)
print("Rectangle after resize:")
print("width=%g, height=%g"%(box.width, box.height))
Page 6
Python Application Programming (15CS664) Module IV
center=find_center(box)
print("The center of resized rectangle is:")
print_point(center)
The statement
box.corner=Point()
indicates that corner is an attribute for the object box and this attribute itself is an object of
the class Point. The following statements indicate that the object box has two more attributes
In this program, we are treating the corner point as the origin in coordinate system and
hence the following assignments –
box.corner.x=0 box.corner.y=0
(Note that, instead of origin, any other location in the coordinate system can be given as
corner point.) Based on all above statements, an object diagram can be drawn as –
Rectangle
The expression box.corner.x means, “Go to the object box refers to and select the attribute
named corner; then go to that object and select the attribute named x.”
➢ The function find_center() takes an object rect as an argument. So, when a call is made using
the statement –
center=find_center(box)
Page 7
Python Application Programming (15CS664) Module IV
A local object p of type Point has been created inside this function. The attributes of p are x
and y, which takes the values as the coordinates of center point of rectangle. Center of a
rectangle can be computed with the help of following diagram.
(x,y)
Half of width
The function find_center() returns the computed center point. Note that, the return value of a
function here is an instance of some class. That is, one can have an instance as return values
from a function.
➢ The function resize() takes three arguments: rect – an instance of Rectangle class and two
numeric variables w and h. The values w and h are added to existing attributes width and
height. This clearly shows that objects are mutable. State of an object can be changed by
modifying any of its attributes. When this function is called with a statement –
resize(box,50,70)
the rect acts as an alias for box. Hence, width and height modified within the function will
reflect the original object box.
Thus, the above program illustrates the concepts: Object of one class is made as attribute for object of
another class, returning objects from functions and objects are mutable.
→ Copying
• An object will be aliased whenever there an object is assigned to another object of same class.
This may happen in following situations –
➢ Direct object assignment (like p2=p1)
➢ When an object is passed as an argument to a function
➢ When an object is returned from a function
• The last two cases have been understood from the two programs in previous sections.
• Let us understand the concept of aliasing more in detail using the following program
>>> class Point:
pass
>>> p1=Point()
>>> p1.x=10
>>> p1.y=20
>>> p2=p1
Page 8
Python Application Programming (15CS664) Module IV
>>> print(p1)
< main .Point object at 0x01581BF0>
>>> print(p2)
< main .Point object at 0x01581BF0>
• Observe that both p1 and p2 objects have same physical memory. It is clear now that the object p2
is an alias for p1.
• So, we can draw the object diagram as below –
p1 x 10 p2
y 20
• Hence, if we check for equality and identity of these two objects, we will get following result.
>>> p1 is p2
True
>>> p1==p2
True
• But, the aliasing is not good always. For example, we may need to create a new object using an
existing object such that – the new object should have a different physical memory, but it must
have same attribute (and their values) as that of existing object. Diagrammatically, we need
something as below –
x 10 x 10
p1 p2
y 20 y 20
>>> p1=Point()
>>> p1.x=10
>>> p1.y=20
Page 9
Python Application Programming (15CS664) Module IV
• Observe that the physical address of the objects p1 and p3 are now different.
• But, values of attributes xand y are same. Now, use the following statements –
>>> p1 is p3
False
>>> p1 == p3
False
• Here, the is operator gives the result as False for the obvious reason of p1 and p3 are being two
different entities on the memory.
• But, why == operator is generating False as the result, though the contents of two objects are
same? The reason is p1 and p3 are the objects of user-defined type.
• And, Python cannot understand the meaning of equality on the new data type. The default
behavior of equality (==) is identity (is operator) itself. Hence, Python applies this default
behavior on p1 == p3and results in False.
NOTE: If we need to define the meaning of equality (==) operator explicitly on user-defined data
types (i.e. on class objects), then we need to override the method eq () inside the class. This will be
discussed later in detail.
• The copy() method of copy module duplicates the object.
• The content (i.e. attributes) of one object is copied into another object as we have discussed till
now.
• But, when an object itself is an attribute inside another object, the duplication will result in a
strange manner.
• To understand this concept, try to copy Rectangle object (created in previous section) as given
below
class Rectangle:
""" This is a class Rectangle.Attributes: width, height and Corner Point """
box1=Rectangle()
box1.corner=Point()
box1.width=100
box1.height=200
box1.corner.x=0
box1.corner.y=0
Page 10
Python Application Programming (15CS664) Module IV
box2=copy.copy(box1)
print(box1 is box2) #prints False
print(box1.corner is box2.corner) #prints True
• Now, the question is – why box1.corner and box2.corner are same objects, when box1 and box2
are different? Whenever the statement is executed,
box2=copy.copy(box1)
• The contents of all the attributes of box1 object are copied into the respective attributes of box2
object.
• That is, box1.width is copied into box2.width, box1.height is copied into box2.height.
• Similarly, box1.corner is copied into box2.corner.
• Now, recollect the fact that corner is not exactly the object itself, but it is a reference to the object
of type Point (Read the discussion done for Figure at the beginning of this Chapter).
• Hence, the value of reference (that is, the physical address) stored in box1.corner is copied into
box2.corner.
• Thus, the physical object to which box1.corner and box2.corner are pointing is only one.
• This type of copying the objects is known as shallow copy.
• To understand this behavior, observe the following diagram
Rectangle Rectangle
box1 width 100 width 100 box2
height 200 height 200
Point
corner corner
x 0
y 0
• Now, the attributes width and height for two objects box1 and box2 are independent.
• Whereas, the attribute corner is shared by both the objects.
• Thus, any modification done to box1.corner will reflect box2.corner as well.
• Obviously, we don’t want this to happen, whenever we create duplicate objects. That is, we want
two independent physical objects.
• Python provides a method deepcopy() for doing this task.
• This method copies not only the object but also the objects it refers to, and the objects they refer
to, and so on.
box3=copy.deepcopy(box1)
print(box1 is box3) #prints False
print(box1.corner is box3.corner) #prints False
Page 11
Python Application Programming (15CS664) Module IV
Thus, the objects box1 and box3 are now completely independent.
→ Debugging
• While dealing with classes and objects, we may encounter different types of errors.
• For example, if we try to access an attribute which is not there for the object, we will get
AttributeError. For example –
>>> p= Point()
>>> p.x = 10
>>> p.y = 20
>>> print(p.z)
AttributeError: 'Point' object has no attribute 'z'
• To avoid such error, it is better to enclose such codes within try/except as given below –
try:
z = p.x
except AttributeError: z = 0
• When we are not sure, which type of object it is, then we can use type() as –
>>> type(box1)
<class ' main .Rectangle'>
• Another method isinstance() helps to check whether an object is an instance of a particular class
>>> isinstance(box1,Rectangle)
True
• When we are not sure whether an object has a particular attribute or not, use a function hasattr() –
>>> hasattr(box1, 'width')
True
• Observe the string notation for second argument of the function hasattr(). Though the attribute
width is basically numeric, while giving it as an argument to function hasattr(), it must be
enclosed within quotes.
Page 12
Python Application Programming (15CS664) Module IV
→ Pure Functions
• To understand the concept of pure functions, let us consider an example of creating a class called
Time. An object of class Time contains hour, minutes and seconds as attributes.
• Write a function to print time in HH:MM:SS format and another function to add two time objects.
• Note that, adding two time objects should yield proper result and hence we need to check whether
number of seconds exceeds 60, minutes exceeds 60 etc, and take appropriate action.
class Time:
"""Represents the time of a day Attributes: hour, minute, second """
def printTime(t):
print("%.2d:%.2d:%.2d"%(t.hour,t.minute,t.second))
def add_time(t1,t2):
sum=Time()
sum.hour = t1.hour + t2.hour
sum.minute = t1.minute + t2.minute
sum.second = t1.second + t2.second
if sum.second >= 60:
sum.second -= 60
sum.minute += 1
if sum.minute >= 60:
sum.minute -= 60
sum.hour += 1
return sum
t1=Time()
t1.hour=10
t1.minute=34
t1.second=25
print("Time1 is:")
printTime(t1)
t2=Time()
t2.hour=2
t2.minute=12
t2.second=41
print("Time2 is :")
printTime(t2)
t3=add_time(t1,t2)
print("After adding two time objects:")
printTime(t3)
Page 13
Python Application Programming (15CS664) Module IV
• Here, the function add_time() takes two arguments of type Time, and returns a Time object,
whereas, it is not modifying contents of its arguments t1 and t2.
• Such functions are called as pure functions.
→ Modifiers
• Sometimes, it is necessary to modify the underlying argument so as to reflect the caller.
• That is, arguments have to be modified inside a function and these modifications should be
available to the caller.
• The functions that perform such modifications are known as modifier function.
• Assume that, we need to add few seconds to a time object, and get a new time.
• Then, we can write a function as below
etc. as a problem involving numbers with base 60 (as every hour is 60 minutes and every minute is
60 seconds), then our code can be improved.
• Such improved versions are discussed later in this chapter.
→ Debugging
• In the program written inabove, we have treated time objects as valid values.
• But, what if the attributes (second, minute, hour) of time object are given as wrong values like
negative number, or hours with value more than 24, minutes/seconds with more than 60 etc? So, it
is better to write error-conditions in such situations to verify the input.
• We can write a function similar to as given below –
def valid_time(time):
if time.hour < 0 or time.minute < 0 or time.second < 0:
return False
return True
• The assert statement clearly distinguishes the normal conditional statements as a part of the logic
of the program and the code that checks for errors.
Page 15
Python Application Programming (15CS664) Module IV
• There will be a tight relationship between the object of the class and the function that operate on
those objects. Hence, the object oriented nature of Python classes will be discussed here.
→ Object-Oriented Features
As an object oriented programming language, Python possess following characteristics:
➢ Programs include class and method definitions.
➢ Most of the computation is expressed in terms of operations on objects.
➢ Objects often represent things in the real world, and methods often correspond to the ways
objects in the real world interact.
• To establish relationship between the object of the class and a function, we must define a function
as a member of the class. \
• function which is associated with a particular class is known as a method.
• Methods are semantically the same as functions, but there are two syntactic differences:
➢ Methods are defined inside a class definition in order to make the relationship
between the class and the method explicit.
➢ The syntax for invoking a method is different from the syntax for calling a function.
• Now onwards, we will discuss about classes and methods.
import math
class Point:
def init (self,a,b):
self.x=a
self.y=b
def dist(self,p2):
d=math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
return d
Page 16
Python Application Programming (15CS664) Module IV
• Let us understand the working of this program and the concepts involved:
➢ Keep in mind that every method of any class must have the first argument as self. The
argument self is a reference to the current object. That is, it is reference to the object which
invoked the method. (Those who know C++, can relate self with this pointer). The object
which invokes a method is also known as subject.
➢ The method init () inside the class is an initialization method, which will be invoked
automatically when the object gets created. When the statement like –
p1=Point(10,20)
is used, the init () method will be called automatically. The internal meaning of the above
line is –
p1. init (10,20)
Here, p1 is the object which is invoking a method. Hence, reference to this object is created
and passed to init () as self. The values 10 and 20 are passed to formal parameters a and b of
init () method. Now, inside init () method, we have statements
self.x=10
self.y=20
This indicates, x and y are instance attributes. The value of x for the object p1 is 10 and, the
value of y for the object p1is 20.
When we create another object p2, it will have its own set of x and y. That is, memory
locations of instance attributes are different for every object.
d=p1.dist(p2)
a reference to the object p1 is passed as self to dist() method and p2 is passed explicitly as a
second argument. Now, inside the dist()method, we are calculating distance between two
point (Euclidian distance formula is used) objects. Note that, in this method, we cannot use
the name p1, instead we will use self which is a reference (alias) to p1.
Page 17
Python Application Programming (15CS664) Module IV
➢ The next method inside the class is str (). It is a special method used for string
representation of user-defined object. Usually, print() is used for printing basic types in
Python. But, user-defined types (class objects) have their own meaning and a way of
representation. To display such types, we can write functions or methods like print_point() as
we did in previous section But, more polymorphic way is to use str () so that, when we
write just print() in the main part of the program, the str () method will be invoked
automatically. Thus, when we use the statement like –
print("P1 is:",p1)
the ordinary print() method will print the portion “P1 is:” and the remaining portion is taken
care by str () method. In fact, str () method will return the string format what we have given
inside it, and that string will be printed by print() method.
→ Operator Overloading
• Ability of an existing operator to work on user-defined data type (class) is known as operator
overloading.
• It is a polymorphic nature of any object oriented programming.
• Basic operators like +, -, * etc. can be overloaded.
• To overload an operator, one needs to write a method within user-defined class.
• Python provides a special set of methods which have to be used for overloading required
operator.
• The method should consist of the code what the programmer is willing to do with the operator.
Following table shows gives a list of operators and their respective Python methods for
overloading.
+ add () <= le ()
- sub () >= ge ()
* mul () == eq ()
/ truediv () != ne ()
% mod () in contains ()
Page 18
Python Application Programming (15CS664) Module IV
class Point:
def _init_ (self,a=0,b=0):
self.x=a
self.y=b
p1=Point(10,20)
p2=Point(4,5)
print("P1 is:",p1)
print("P2 is:",p2)
p4=p1+p2 #call for add () method
print("Sum is:",p4)
• In the above program, when the statement p4 = p1+p2 is used, it invokes a special method _add
() written inside the class. Because, internal meaning of this statement is–
p4 = p1. add (p4)
Here, p1 is the object invoking the method. Hence, self inside _add () is the reference (alias) of p1.
And, p4 is passed as argument explicitly.
In the definition of add (), we are creating an object p3with the statement –
p3=Point()
The object p3 is created without initialization. Whenever we need to create an object with and without
initialization in the same program, we must set arguments of init () for some default values. Hence, in
the above program arguments a and b of init () are made as default arguments with values as zero.
Thus, x and y attributes of p3will be now zero. In the add () method, we are adding respective attributes
of self and p2 and storing in p3.x and p3.y. Then the object p3 is returned. This returned object is
received as p4and is printed.
Page 19
Python Application Programming (15CS664) Module IV
NOTE that, in a program containing operator overloading, the overloaded operator behaves in a
normal way when basic types are given. That is, in the above program, if we use the statements
m= 3+4
print(m)
it will be usual addition and gives the result as 7. But, when user-defined types are used as operands,
then the overloaded method is invoked.
class Time:
def init (self, h=0,m=0,s=0):
self.hour=h
self.min=m
self.sec=s
def time_to_int(self):
minute=self.hour*60+self.min
seconds=minute*60+self.sec
return seconds
def _ eq (self,t):
return self.hour==t.hour and self.min==t.min and self.sec==t.sec
self.int_to_time(seconds)
T1=Time(3,40)
T2=Time(5,45)
print("T1 is:",T1)
print("T2 is:",T2)
print("Whether T1 is same as T2?",T1==T2) #call for eq ()
print("T1+T2 is:",T3)
T6=sum([T1,T2,T3,T4])
print("Using sum([T1,T2,T3,T4]):",T6)
→ Debugging
• We have seen earlier that hasattr() method can be used to check whether an object has particular
attribute.
• There is one more way of doing it using a method vars(). This method maps attribute names and
their values as a dictionary.
• For example, for the Point class defined earlier, use the statements
>>> p = Point(3, 4)
>>> vars(p) #output is {'y': 4, 'x': 3}
• For purposes of debugging, you might find it useful to keep this function handy:
def print_attributes(obj):
for attr in vars(obj):
print(attr, getattr(obj, attr))
• Here, print_attributes() traverses the dictionary and prints each attribute name and its
corresponding value.
• The built-in function getattr() takes an object and an attribute name (as a string) and returns the
attribute values
Page 22