BPLCK205B Module 5 Notes
BPLCK205B Module 5 Notes
Module 5
Textbook 2: Chapters 15 – 17
RBT: L1, L2, L3
Textbook 1: Al Sweigart, “Automate the Boring Stuff with Python”, 1st Edition, No Starch Press,
2015. (Available under CC-BY-NC-SA license at https://automatetheboringstuff.com/)
• In mathematical notation, points are often written in parentheses with a comma separating the
coordinates.
• For example, (0, 0) represents the origin, and (x, y) represents the point x units to the right and y
units up from the origin.
• A programmer-defined type is also called a class. A class definition looks like this:
class Point: """Represents a point in 2-D space."""
>>> obj=Point()
>>> obj #<__main__.Point object at 0x0000018B1D134EB0>
• The class has a documentation string, which can be accessed via ClassName.__doc__. The class_suite
consists of all the component statements defining class members, data attributes and functions.
• Syntax
class MyClass:
# member variables
variable1 = something
variable2 = something
# member functions / Methods
def function1(self, parameter1, ...):
self.variable1 = something else
# defining a new variable
self.variable3 = something
function1 statements...
def function2(self, parameter1, ...):
self.variable2 = something else
function2 statements...
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 4 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
• Example
class MyClass:
x=5
p1 = MyClass() #p1 is an object of MyClass
print(p1.x) #5→print value of x using dot op
Note: The __init__() function is called automatically every time the class is being used to create a
new object.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person("Raju", 36)
print(p1.name) #Raju
print(p1.age) #36
• Objects can also contain methods. Methods in objects are functions that belong to the object.
• Example: Insert a function that prints a greeting, and execute it on the p1 object:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(self):
print("Hello my name is " + self.name)
p1 = Person("Raju", 36)
p1.myfunc()
Self parameter
• The self parameter is a reference to the current instance of the class, and is used to access variables that
belongs to the class. It does not have to be named self, you can call it whatever you like, but it has to
be the first parameter of any function in the class:
• Example: Use the words myobj and abc instead of self:
class Person:
def __init__(myobj, name, age):
myobj.name = name
myobj.age = age
def myfunc(abc): #can use myobj, also instead of abc
print("Hello my name is " + abc.name) #displays name
p1 = Person("Raju", 36)
p1.myfunc()
p1 = Person("Raju", 36)
p1.age = 40
p1.myfunc()
print(p1.age) Modify Object Properties
p1 = Person("Raju", 36)
del p1.age
print(p1.age)
5.3. Attributes
• We can assign values to an instance using dot notation:
>>> class Point:
pass
>>> blank = Point()
>>> blank.x = 3.0
>>> blank.y = 4.0
>>>blank1=Point()
>>>blank1.a=10
>>>blank1.b=20
• The variable blank refers to a Point object, which contains two attributes. Each attribute refers to
a floating-point number.
>>> blank.y #4.0
>>> x = blank.x
>>> x #3.0
• There is no conflict between the variable x and the attribute x. Use dot notation as part of any
expression. For example:
>>> '(%g, %g)' % (blank.x, blank.y) #'(3.0, 4.0)'
>>> import math
>>> distance = math.sqrt(blank.x**2 + blank.y**2)
>>> distance
5.4. Rectangles
• To design a class to represent rectangles.
• The attributes of an object can be of two possibilities: You could specify one corner of the rectangle
(or the center), the width, and the height. You could specify two opposing corners.
• Here is the class definition:
class Rectangle:
"""Represents a rectangle.
attributes: width, height, corner."""
• To represent a rectangle, you have to instantiate a Rectangle object and assign values to the
attributes:
>>> box = Rectangle()
>>> box #<__main__.Rectangle object at 0x03D1C50>
>>>box.width = 100.0 #box.width #100.0
>>>box.height = 200.0 #box.height #200.0
>>>box.corner = Point() #<__main__.Point object at 0x03D1BEB0>
>>>box.corner.x = 0.0
>>>box.corner.y = 0.0
• The expression box.corner.x means, “Go to the object box and select the attribute named corner;
then go to that object and select the attribute named x.”
box → corner → x
• Example of class Employee: To display employee details and count number of employees
class Employee:
#Common base class for all employees
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
• For example, find_center takes a Rectangle as an argument and returns a Point that contains the
coordinates of the center of the Rectangle:
class Point:
"""Represents a point in 2-D space."""
class Rectangle:
"""Represents a rectangle attributes: width, height, corner."""
def find_center(rect):
#Returns a Point at the center of a Rectangle, returns: new Point
p = Point()
p.x = rect.corner.x + rect.width/2
p.y = rect.corner.y + rect.height/2
return p
box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0
center=find_center(box)
print(center.x,center.y) #50, 100
box = Rectangle()
box.width = 100.0
box.height = 200.0
#changing the values of objects
box.width = box.width + 50
box.height = box.height + 100
5.7. Copying
• Copying an object is often an alternative to aliasing. The copy module contains a function called
copy that can duplicate any object:
>>> import copy
>>> class Point:
pass
>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> p2 = copy.copy(p1) #p1 and p2 contain the same data, but they are not the same Point.
>>> p1 is p2 #False
>>> p1 == p2 #False
• The default behavior of the == operator is the same as the is operator; it checks object identity,
not object equivalence. If you use copy.copy to duplicate a Rectangle, you will find that it copies
the Rectangle object but not the embedded Point.
>>> box2 = copy.copy(box)
>>> box2 is box #False
>>> box2.corner is box.corner #True
• Operation is shallow copy because it copies the object and any references it contains, but not
the embedded objects.
Shallow Copying
• copy.copy copies the object but not the embedded object this operation is known as shallow
copy.
import copy
class Point:
pass
class Box:
pass
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 12 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
b = Box()
b.width = 100.0
b.height = 200.0
b.corner = Point()
b.corner.x = 0.0
b.corner.y = 0.0
b1 = copy.copy(b)
print(b is b1)
print(b.corner is b1.corner)
Deep Copying
• The copy module provides a method named deepcopy that copies not only the object but also the
objects it refers to, and the objects they refer to, and so on.
>>> box3 = copy.deepcopy(box)
>>> box3 is box #False
>>> box3.corner is box.corner #False
b = Box()
b.width = 100.0
b.height = 200.0
b.corner = Point()
b.corner.x = 0.0
b.corner.y = 0.0
b1 = copy.deepcopy(b)
print(b is b1)
print(b.corner is b1.corner)
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 13 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
p1=Point()
p1.x=3.0
p1.y=4.0
print("p1 Contents: ")
print_point(p1)
p2=copy.copy(p1)
print("p2 Contents: ")
print_point(p2)
print("p1 is p2: "), p1 is p2)
print("p1 == p2: ", p1==p2)
box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0
box3 = copy.deepcopy(box)
print("box Contents: ",box.width,box.height,box.corner.x,box.corner.y)
print("box3Contents: ",box3.width,box3.height,box3.corner.x,box3.corner.y)
print("box3 is box: ",box3 is box )
print("box3.corner is box.corner:", box3.corner is box.corner)
Output:
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 14 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
Debugging
• If we try to access an attribute that does not exist we get an AttributeError
class Point:
pass
p = Point()
p.x =10
p.y= 20
print(p.x)
print(p.y)
print(p.z) # AttributeError
isinstance function can be used to check if an object is instance of a class
>>> isinstance(p,Point) # returns True
hasattr function is used to check if object has a particular attribute or not
>>> hasattr(p,’x’) # returns True
>>> hasattr(p,’z’) # returns False
duration.second = 2
done = add_time(start, duration)
print_time(done) # (11:23:11)
5.10. Modifiers
• Sometimes it is useful for a function to modify the objects it gets as parameters. In that case, the
changes are visible to the caller.
• Functions that work this way are called modifiers. increment, which adds a given number of
seconds to a Time object, can be written naturally as a modifier.
def print_time(t):
"""Print a Time object in human-readable format."""
print("(%.2d:%.2d:%2d)" % (t.hour, t.minute, t.second))
def int_to_time(seconds):
time = Time()
minutes, time.second = divmod(seconds, 60)
time.hour, time.minute = divmod(minutes, 60)
return time
t = Time()
sec = 35320
t = int_to_time(sec)
print_time(t) # (09:48:40)
Modified program to add two time objects using time to integer and integer to time functions
class Time:
"""Represents the time of day, attributes: hour, minute, second""“
def print_time(t):
"""Print a Time object in human-readable format."""
print("(%.2d:%.2d:%2d)" % (t.hour, t.minute, t.second))
def time_to_int(time):
minutes = time.hour * 60 + time.minute
seconds = minutes * 60 + time.second
return seconds
def int_to_time(seconds):
time = Time()
minutes, time.second = divmod(seconds, 60)
time.hour, time.minute = divmod(minutes, 60)
return time
def add_time(t1, t2):
seconds = time_to_int(t1) + time_to_int(t2)
return int_to_time(seconds)
t1 = Time()
t1.hour = 9
t1.minute = 48
t1.second = 9
t2 = Time()
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 21 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
t2.hour = 10
t2.minute = 50
t2.second = 23
t3=add_time(t1,t2)
print_time(t3) #(20:38:32)
• The syntax for invoking a method is different from the syntax for calling a function.
o Method is called by its name, but it is associated to an object (dependent).
o A method is implicitly passed the object on which it is invoked.
o It may or may not return any data.
o A method can operate on the data (instance variables) that is contained by the corresponding
class
class Time:
def print_time(time):
print('%.2d:%.2d:%.2d' % (time.hour, time.minute,
time.second))
#Method of Time
def increment(self, seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
#Method of Time
def time_to_int(self):
minutes = self.hour * 60 + self.minute
seconds = minutes * 60 + self.second
return seconds
def print_time(self):
print("%.2d:%.2d:%.2d" % (self.hour, self.minute, self.second))
return time
start = Time()
start.hour = 9
start.minute = 45
start.second = 00
start.print_time() #09:45:00
end = start.increment(1337)
end.print_time() #10:07:17
def print_time(self):
print("%.2d:%.2d:%.2d" % (self.hour, self.minute, self.second))
#Method of Time
def is_after(self, other):
return self.time_to_int() > other.time_to_int()
def int_to_time(seconds):
time = Time()
minutes, time.second = divmod(seconds, 60)
time.hour, time.minute = divmod(minutes, 60)
return time
start = Time()
start.hour = 9
start.minute = 45
start.second = 00
start.print_time() #09:45:00
end = start.increment(1337)
end.print_time() #10:07:17
print(end.is_after(start)) #True
Program to use __init__ method by taking variable arguments (no arg, one arg, two arg ..)
class Time:
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
def print_time(self):
print("%.2d:%.2d:%.2d" % (self.hour, self.minute, self.second))
Class Time:
# inside class Time:
def __str__(self):
return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
When you print an object, Python invokes the str method:
>>> time = Time(9, 45)
>>> print(time) #09:45:00
Ob1 = complex(1, 2)
Ob2 = complex(2, 3)
Ob3 = Ob1 + Ob2
print(Ob3) #(3, 5)
start = Time(9,45,30)
duration = Time(1,35,40)
print(start + duration) #11:21:10
seconds += self.time_to_int()
return int_to_time(seconds)
• Here are examples that use the + operator with different types:
>>> start = Time(9, 45)
>>> duration = Time(1, 35)
>>> print(start + duration) #11:20:00
>>> print(start + 1337) #10:07:17
start = Time(9,45,30)
duration = Time(1,35,40)
print(start + duration) #11:21:10
print(start + 1337) #10:07:47
• Unfortunately, this implementation of addition is not commutative. If the integer is the first
operand, you get
>>> print(1337 + start)
TypeError: unsupported operand type(s) for +: 'int' and 'instance‘
• The problem is, instead of asking the Time object to add an integer, Python is asking an integer
to add a Time object, and it doesn’t know how.
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 34 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
• The special method __radd__, which stands for “right-side add”. This method is invoked when a
Time object appears on the right side of the + operator.
# inside class Time:
def __radd__(self, other):
return self.__add__(other)
And here’s how it’s used:
>>> print(1337 + start) #10:07:47
5.18. Polymorphism
• Type-based dispatch is useful when it is necessary, but (fortunately) it is not always necessary.
Often you can avoid it by writing functions that work correctly for arguments with different types.
• Example: histogram to count the number of times each letter appears in a word.
def histogram(s):
d = dict()
for c in s:
if c not in d:
d[c] = 1
else:
d[c] = d[c]+1
return d
• This function not only works on strings but also works for lists, tuples, and even dictionaries,
as long as the elements of s are hashable, so they can be used as keys in d.
>>> t = ['spam', 'egg', 'spam', 'spam', 'bacon', 'spam']
>>> histogram(t) #{'spam': 4, 'egg': 1, 'bacon': 1}
• Functions that work with several types are called polymorphic. Polymorphism can facilitate
code reuse.
• For example, the built-in function sum, which adds the elements of a sequence, works as long as
the elements of the sequence support addition.
• Since Time objects provide an add method, they work with sum:
>>> t1 = Time(7, 43)
>>> t2 = Time(7, 41)
>>> t3 = Time(7, 37)
print(add(2)) #2
print(add(2, 3)) #5
print(add(2, 3, 4)) #9
• But, if we does not initialize with default values, then python will raise error:
Print(add())
class USA:
def capital(self):
print("Washington, D.C. is the capital of USA.")
def language(self):
print("English is the primary language of USA.")
Department of AI&ML, Vemana IT Prepared by: Dr. Kantharaju H C Page 36 of 38
Introduction to Python Programming (BPLCK205B) Module 5: Classes and Objects, Functions, Methods
def type(self):
print("USA is a developed country.")
def func(obj):
obj.capital()
obj.language()
obj.type()
obj_ind = India()
obj_usa = USA()
func(obj_ind)
func(obj_usa)
Output:
New Delhi is the capital of India.
Hindi is the most widely spoken language of India.
India is a developing country.
Washington, D.C. is the capital of USA.
English is the primary language of USA.
USA is a developed country.
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()
obj_bird.intro()
obj_bird.flight()
obj_spr.intro()
obj_spr.flight()
obj_ost.intro()
obj_ost.flight()
There are many types of birds.
Most of the birds can fly but some cannot.
There are many types of birds.
Sparrows can fly.
There are many types of birds.
Ostriches cannot fly.