Object Oriented Programming With Python
Object Oriented Programming With Python
OBJECT-ORIENTEDPROGRAMMING
Object-OrientedProgramming(OOP) is a programming language model organizedaround
objects ratherthanactionsanddata.Anobject-orientedprogramcanbecharacterizedasdatacontrollingaccess to
code.Concepts of OOPS
Object
Class
Inheritance
Polymorphism
Abstraction
Encapsulation
OBJECT
Object means a real word entity such as pen, chair, table etc. Any entity that has state and behavior
isknown as an object. Object can be defined as an instance of a class. An object contains an address and
takesup some space in memory. Objects can communicate without knowing details of each other's data or
code,the onlynecessarythingis that thetypeofmessageacceptedand typeof response returned bythe objects.
Anobjecthasthreecharacteristics:
state:representsdata(value)ofanobject.
behavior:representsthe behavior(functionality)ofanobjectsuch asdeposit,withdrawetc.
identity: Object identity is typically implemented via a unique ID. The value of the ID is
notvisibleto the externaluser. But, itis used internallybythe JVM toidentifyeach object uniquely.
CLASS
Collection of objects is called class. It is a logical entity. A class can also be defined as a
blueprintfrom which you can create an individual object. A class consists of Data members and methods.
The primarypurposeofaclassistoholddata/information.The memberfunctions determinethebehavioroftheclass,
i.e. provide a definition for supporting various operations on data held in the form of an object.Class
doesn’tstoreanyspace.
class ClassName:
'Brief description of the class (Optional)'
# Code ...
1
To define a class, we use the class keyword, followed by the name of the class and the colon (:). The first
line in the class body is a string that briefly describes this class. (Optional), we can access this string
via ClassName.__doc__
In the class body, you can declare attributes, methods, and constructors.
Attribute:
The attribute is a member of the class. For example, the rectangle has two attributes including width and
height.
Method:
The method of class is similar to a normal function but it is a function of class, in order to use it, we need
to call through object.
The first parameter of the method is always self (a keyword that refers to the class itself).
Constructor:
Constructor is a special method of class, which is always named __init__.
The first parameter of the constructor is always self (a keyword refers to the class itself).
Constructor is used to create an object.
Constructor assigns values of the parameter to the properties of the object that will be created.
We can only define a constructor in class.
If the class is not defined by the constructor, Python assumes that it inherit constructor of parent class.
#rectangle.py
# Rectangle.
class Rectangle :
'This is Rectangle class'
# Method to create object (Constructor)
def __init__(self, width, height):
self.width= width
self.height = height
def getWidth(self):
return self.width
def getHeight(self):
return self.height
2
# Method to calculate Area.
def getArea(self):
return self.width * self.height
#testRectangle.py
Output
3
What happens when you create an object from a class?
When you create an object of the Rectangle class, the constructor of that class will be called to create an object,
and the attributes of the object will be assigned the value from the parameter. It is similar to the illustration
below:
#person.py
class Person :
# The age and gender parameters has a default value.
def __init__ (self, name, age = 1, gender = "Male" ):
self.name = name
self.age = age
self.gender= gender
4
def showInfo(self):
print ("Name: ", self.name)
print ("Age: ", self.age)
print ("Gender: ", self.gender)
#testPerson.py
from person import Person
# Create an object of Person.
aimee = Person("Aimee", 21, "Female")
aimee.showInfo()
print (" --------------- ")
# default age, gender.
alice = Person( "Alice" )
alice.showInfo()
print (" --------------- ")
# Default gender.
tran = Person("Tran", 37)
tran.showInfo()
Output
5
ATTRIBUTES
In Python, there are 2 similar concepts that we need to distinguish them:
1. Attribute
2. Class's variable
Objects created from one class will be located at different addresses on memory, and their "same name"
attributes also have different addresses on memory. For example:
#testAttributePlayer.py
from player import Player
player1 = Player("Tom", 20)
player2 = Player("Jerry", 20)
6
print ("player1.name = ", player1.name)
print ("player1.age = ", player1.age)
print ("player2.name = ", player2.name)
print ("player2.age = ", player2.age)
print (" ------------ ")
print ("Assign new value to player1.age = 21 ")
# Assgin new value to age attribute of player1.
player1.age = 21
print ("player1.name = ", player1.name)
print ("player1.age = ", player1.age)
print ("player2.name = ", player2.name)
print ("player2.age = ", player2.age)
Output
Python allows you to create a new attribute for a pre-existing object. For example, the player1 object and the
new attribute named as address.
7
#testNewAttributePlayer.py
Output:
8
Function Attributes
Normally, you access to the attribute of an object through the "dot" operator (e.g player1.name).
However Python allows you to access to it via function.
Function Description
Returns the value of the attribute, or returns the default value if the
getattr(obj, name[, default])
object does not have this attribute.
Check whether this object has an attribute given by the parameter
hasattr(obj,name)
'name' or not.
setattr(obj,name,value) Set value to attribute. If the attribute does not exist, it will be created.
delattr(obj, name) Delete attribute.
#testAttFunctions.py
from player import Player
player1 = Player("Tom", 20)
# getattr(obj, name[, default])
print ("getattr(player1,'name') = " ,getattr(player1,"name") )
print ("setattr(player1,'age', 21): ")
# setattr(obj,name,value)
setattr(player1,"age", 21)
print ("player1.age = ", player1.age)
9
# Check player1 has 'address' attribute?
hasAddress= hasattr(player1, "address")
print ("hasattr(player1, 'address') ? ", hasAddress)
# Create attribute 'address' for object 'player1'
print ("Create attribute 'address' for object 'player1'")
setattr(player1, 'address', "USA")
print ("player1.address = ", player1.address)
# Delete attribute 'address'.
Delattr(player1, “address”)
Output:
#testBuildInAttributes.py
class Customer :
'This is Customer class'
def __init__(self, name, phone, address):
1
0
self.name = name
self.phone = phone
self.address = address
john = Customer("John",1234567, "USA")
print ("john.__dict__ = ", john.__dict__)
print ("john.__doc__ = ", john.__doc__)
print ("john.__class__ = ", john.__class__)
print ("john.__class__.__name__ = ", john.__class__.__name__)
print ("john.__module__ = ", john.__module__)
Output:
Class's Variables
In Python, Class's Variable concept is equivalent to static Field concept of other languages such
as Java, CSharp. Variables of the class can be accessed via the class name or the object.
1
1
Each of the Class's Variable has an address on memory. And it is shared with all the objects of class.
#testVariablePlayer.py
Output:
DATA MODELING
Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations
between objects. (In a sense, and in conformance to Von Neumann’s model of a “stored program computer”,
code is also represented by objects.)
In Python, everything is an object. Each object has an identity, a type and a value.
Here, the id() gives us the identity that we were talking about, which is a unique integer that never changes
during the lifetime of an object. We can think of identity as an object’s address in memory.
1
3
Example: We can check if two objects are the same with is operator.
>>> id(player_copy)
139780790567600
>>> type(age)
<class ‘int’>
>>> type(player_1)
<class 'str'>
The value of an object can be changed if the object is mutable and if the object is immutable the value of it
cant be changed.
The value of an object is what the object is holding in it. Here 53 is the valueof age and Maradona is the value
of player_1 . The value of an object can be changed if the object is mutable and if the object is immutable the
value of it can’t be changed.
age = 53 is an integer type object with value 53 and has the id 9753824
That means the age = 53 is an integer type object with value 53 and has the id 9753824 and if we now change
So, if we want to change the value of player_1 then both objects player_copy and player_1 will not show the
same identity.
1
4
>>> player_1 = "Messi" # assigning new value to
>>>player_copy
'Maradona'
>>> player_1 is player_copy
False
In the above example, we mutated the players list by changing the first value and using append() method.
Asboth players and players_copy referring to the same list object, changes in one list will have an effect on the
other variable.
Persistence denotes a process or an object that continues to exist even after its parent process or object ceases, or
the system that runs it is turned off. When a created process needs persistence, non-volatile storage, including a
hard disk, is used instead of volatile memory like RAM.
For example, imagine that you're using the Chrome browser on the Windows operating system and, due to some
technical issues, the browser process was shut down or killed. The browser restarts the next time you open it and
attempts to reopen any tabs that were open when it crashed. A persistent process thus exists even if it failed or
was killed for some technical reasons. The persistent state is the state that remains even after a process shuts
1
5
down. Persistent process states are stored in persistent storage (non-volatile storage like hard drives) before the
system fails or shuts down. They're easily retrieved once the system is turned on. The core processes of the
operating system must be persistent for maintaining the data, device, or workspace in a similar state when the
system is switched on.
Types of Persistence
There are two types of persistence: object persistence and process persistence. In data terms, object
persistence refers to an object that is not deleted until a need emerges to remove it from the memory. Some
database models provide mechanisms for storing persistent data in the form of objects.
In process persistence, processes are not killed or shut down by other processes and exist until the user kills
them. For example, all of the core processes of a computer system are persistent for enabling the proper
functioning of the system. Persistent processes are stored in non-volatile memory. They do not need special
databases like persistent objects.
Purpose:The shelve module implements persistent storage for arbitrary Python objects which can be
pickled,using a dictionary-like API.
The shelve module can be used as a simple persistent storage option for Python objects when a relational database
is overkill. The shelf is accessed by keys, just as with a dictionary. The values are pickled and written to a database
created and managed by anydbm.
importshelve
s=shelve.open('test_shelf.db')
try:
s['key1']={'int':10,'float':9.5,'string':'Sample data'}
finally:
s.close()
1
6
To access the data again, open the shelf and use it like a dictionary:
importshelve
s=shelve.open('test_shelf.db')
try:
existing=s['key1']
finally:
s.close()
printexisting
$ python shelve_create.py
$ python shelve_existing.py
The dbm module does not support multiple applications writing to the same database at the same time. If you
know your client will not be modifying the shelf, you can tell shelve to open the database read-only.
importshelve
s=shelve.open('test_shelf.db',flag='r')
try:
existing=s['key1']
finally:
s.close()
printexisting
If the program tries to modify the database while it is opened read-only, an access error exception is generated. The
exception type depends on the database module selected by anydbm when the database was created.
Write-back
Shelves do not track modifications to volatile objects, by default. That means if we change the contents of an item
stored in the shelf, we must update the shelf explicitly by storing the item again.
importshelve
s=shelve.open('test_shelf.db')
try:
prints['key1']
1
7
s['key1']['new_value']='this was not here before'
finally:
s.close()
s=shelve.open('test_shelf.db',writeback=True)
try:
prints['key1']
finally:
s.close()
In this example, the dictionary at ‘key1’ is not stored again, so when the shelf is re-opened, the changes have not
been preserved.
$ python shelve_create.py
$ python shelve_withoutwriteback.py
To automatically catch changes to volatile objects stored in the shelf, open the shelf with writeback enabled. The
writeback flag causes the shelf to remember all of the objects retrieved from the database using an in-memory
cache. Each cache object is also written back to the database when the shelf is closed.
importshelve
s=shelve.open('test_shelf.db',writeback=True)
try:
prints['key1']
s['key1']['new_value']='this was not here before'
prints['key1']
finally:
s.close()
s=shelve.open('test_shelf.db',writeback=True)
try:
prints['key1']
finally:
s.close()
Although it reduces the chance of programmer error, and can make object persistence more transparent, using
writeback mode may not be desirable in every situation. The cache consumes extra memory while the shelf is open,
and pausing to write every cached object back to the database when it is closed can take extra time. Since there is
no way to tell if the cached objects have been modified, they are all written back. If your application reads data
more than it writes, writeback will add more overhead than you might want.
$ python shelve_create.py
$ python shelve_writeback.py
1
8
{'int': 10, 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}
INHERITANCE
Inheritance allows us to define a class that inherits all the methods and properties from another class.
Parent class is the class being inherited from, also called base class.
Child class is the class that inherits from another class, also called derived class.
Any class can be a parent class, so the syntax is the same as creating any other class:
Example
class Person:
def __init__(self, fname, lname):
1
9
self.firstname = fname
self.lastname = lname
def printname(self):
print(self.firstname, self.lastname)
#Use the Person class to create an object, and then execute the printname method:
x = Person("John", "Doe")
x.printname()
To create a class that inherits the functionality from another class, send the parent class as a parameter when
creating the child class:
Example
Create a class named Student, which will inherit the properties and methods from the Person class:
class Student(Person):
pass
Note: Use the pass keyword when we do not want to add any other properties or methods to the class.
Now the Student class has the same properties and methods as the Person class.
Example
x = Student("Mike", "Olsen")
x.printname()
So far we have created a child class that inherits the properties and methods from its parent.
Example
2
0
class Student(Person):
def __init__(self, fname, lname):
#add properties etc.
When you add the __init__() function, the child class will no longer inherit the parent's __init__() function.
class Student(Person):
def __init__(self, fname, lname):
Person.__init__(self, fname, lname)
Now we have successfully added the __init__() function, and kept the inheritance of the parent class, and we are
ready to add functionality in the __init__() function.
Python also has a super() function that will make the child class inherit all the methods and properties from its
parent:
class Student(Person):
def __init__(self, fname, lname):
super().__init__(fname, lname)
By using the super() function, you do not have to use the name of the parent element, it will automatically inherit
the methods and properties from its parent.
Add Properties
class Student(Person):
def __init__(self, fname, lname):
super().__init__(fname, lname)
self.graduationyear = 2019
In the example below, the year 2019 should be a variable, and passed into the Student class when creating
student objects. To do so, add another parameter in the __init__() function:
Add a year parameter, and pass the correct year when creating objects:
class Student(Person):
2
1
def __init__(self, fname, lname, year):
super().__init__(fname, lname)
self.graduationyear = year
x = Student("Mike", "Olsen", 2019)
Add Methods
Add a method called welcome to the Student class:
class Student(Person):
def __init__(self, fname, lname, year):
super().__init__(fname, lname)
self.graduationyear = year
def welcome(self):
print("Welcome", self.firstname, self.lastname, "to the class of",
self.graduationyear)
POLYMORPHISM
When onetaskisperformedbydifferentways
i.e.knownaspolymorphism.Forexample:toconvincethecustomer differently, to drawsomethinge.g.
shapeorrectangleetc.
Operator overloading
Operator overloading is the type of overloading in which an operator can be used in multiple ways beyond
its predefined meaning.
>>>print(2*7)
14
>>>print(”a”*3)
aaa
So in the above example the multiplication operator did the multiplication of two numbers in the first
one, but in the second one as the multiplication of a string and an integer is not possible so the
character is printed repeatedly for 3 times. So it shows how we can use a single operator in different
way.
Example 1:
class Vehicle:
def __init__(self, fare):
self.fare = fare
2
3
bus= Vehicle(20)
car= Vehicle(30)
total_fare=bus+ car
print(total_fare)
Output:
Traceback (most recent call last):
File “G:\python pycharm project\main.py”, line 7, in <module>
total_fare=bus+ car
TypeError: unsupported operand type(s) for +: ‘Vehicle’ and ‘Vehicle’
Here in the above example an error has occurred, this is because python has no idea how to add two objects
together. Here Vehicle is the object.
Now comes the application of operator overloading here.
Now we will overload the special function __add__ operator.
class Vehicle:
def __init__(self, fare):
self.fare = fare
def __add__(self, other)://using the special function __add__ operator
return self.fare+ other.fare
bus= Vehicle(20)
car= Vehicle(30)
total_fare=bus+ car
print(total_fare)
Output:
50
Overloading the special function magically defines everytime we use plus operator in the object
total_fare=bus+ car we are going to add their fares.
Method Overloading
Method overloading means a class containing multiple methods with the same name but may have different
arguments. Basically python does not support method overloading, but there are several ways to achieve
method overloading. Though method overloading can be achieved, the last defined methods can only be
usable.
Example:
class Area:
def find_area(self, a=None, b=None):
if a != None and b != None:
print("Rectangle:", (a * b))
elifa != None:
2
4
print("square:", (a * a))
else:
print("No figure assigned")
obj1=Area()
obj1.find_area()
obj1.find_area(10)
obj1.find_area(10,20)
Output
No figure assigned
square: 100
Rectangle: 200
In the above example if no arguments are passed during function call then it will show no value assigned, if
we pass a single argument then it will show the area of square and if 2 values are passed then it will show
the area of rectangle.
ABSTRACT CLASSES
A class is called an Abstract class if it contains one or more abstract methods. An abstract method is a
method that is declared, but contains no implementation. Abstract classes may not be instantiated, and its
abstract methods must be implemented by its subclasses.
An abstract class cannot be instantiated. It just provides an interface for subclasses to avoid code
duplication. It makes no sense to instantiate an abstract class.
A derived subclass must implement the abstract methods to create a concrete class that fits the
interface defined by the abstract class. Therefore it cannot be instantiated unless all of its abstract
methods are overridden.
Python comes with a module called abc which provides useful stuff for abstract class.
We can define a class as an abstract class by abc.ABC and define a method as an abstract method
by abc.abstractmethod. ABC is the abbreviation of abstract base class.
There are many built-in ABCs in Python. ABCs for Data structures like Iterator, Generator, Set, mapping
etc. are defined in collections.abc module. The numbers module defines numeric tower which is a collection
of base classes for numeric data types. The 'abc' module in Python library provides the infrastructure for
2
5
defining custom abstract base classes.
'abc' works by marking methods of the base class as abstract. This is done by @absttractmethod decorator. A
concrete class which is a sub class of such abstract base class then implements the abstract base by overriding
its abstract methods.
The abc module defines ABCMeta class which is a metaclass for defining abstract base class. Following
example defines Shape class as an abstract base class using ABCMeta. The shape class has area() method
decorated by abstractmethod.
A Rectangle class now uses above Shape class as its parent and implementing the abstract area() method.
Since it is a concrete class, it can be instantiated and imlementedarea() method can be called.
importabc
classShape(metaclass=abc.ABCMeta):
@abc.abstractmethod
def area(self):
pass
classRectangle(Shape):
def __init__(self,x,y):
self.l= x
self.b=y
def area(self):
returnself.l*self.b
r =Rectangle(10,20)
print('area: ',r.area())
Note the abstract base class may have more than one abstract methods. The child class must implement all of
them failing which TypeError will be raised.
The abc module also defines ABC helper class which can be used instead of ABCMeta class in definition of
abstract base class.
class Shape(abc.ABC):
@abc.abstractmethod
def area(self):
pass
Instead of subclassing from abstract base class, it can be registered as abstract base by register class
2
6
decorator.
classShape(abc.ABC):
@abc.abstractmethod
def area(self):
pass
@Shape.register
classRectangle():
def __init__(self,x,y):
self.l= x
self.b=y
def area(self):
returnself.l*self.b
We may also provide class methods and static methods in abstract base class by decorators
@abstractclassmethod and @abstractstatic method decorators respectively.
EXCEPTION HANDLING
We can make certain mistakes while writing a program that lead to errors when we try to run it. A python
program terminates as soon as it encounters an unhandled error. These errors can be broadly classified into two
classes:
1. Syntax errors
2. Logical errors (Exceptions)
>>>if a <3
File "<interactive input>", line 1
if a <3
^
SyntaxError: invalid syntax
2
7
As shown in the example, an arrow indicates where the parser ran into the syntax error.
We can notice here that a colon : is missing in the if statement.
Python has many built-in exceptions that are raised when your program encounters an error (something in the
program goes wrong).
When these exceptions occur, the Python interpreter stops the current process and passes it to the calling process
until it is handled. If not handled, the program will crash.
For example, let us consider a program where we have a function A that calls function B, which in turn calls
function C. If an exception occurs in function C but is not handled in C, the exception passes to B and then
to A.
If never handled, an error message is displayed and our program comes to a sudden unexpected halt.
print(dir(locals()['__builtins__']))
2
8
GeneratorExit Raise when a generator's close() method is called.
KeyboardInterrupt Raised when the user hits the interrupt key (Ctrl+C or Delete).
RuntimeError Raised when an error does not fall under any other category.
2
9
type.
ZeroDivisionError Raised when the second operand of division or modulo operation is zero.
3
0
A single try statement can have multiple except statements. This is useful when the try block contains
statements that may throw different types of exceptions.
You can also provide a generic except clause, which handles any exception.
After the except clause(s), you can include an else-clause. The code in the else-block executes if the code
in the try: block does not raise an exception.
The else-block is a good place for code that does not need the try: block's protection.
We can thus choose what operations to perform once we have caught the exception. Here is a simple example.
randomList = ['a', 0, 2]
Output
The entry is a
Oops! <class 'ValueError'> occurred.
Next entry.
The entry is 0
Oops! <class 'ZeroDivisionError'>occured.
Next entry.
The entry is 2
The reciprocal of 2 is 0.5
In this program, we loop through the values of the randomList list. As previously mentioned, the portion that can
cause an exception is placed inside the try block.
If no exception occurs, the except block is skipped and normal flow continues(for last value). But if any
exception occurs, it is caught by the except block (first and second values).
3
1
Here, we print the name of the exception using the exc_info() function inside sys module. We can see
that a causes ValueError and 0 causes ZeroDivisionError.
Python try...finally
We can use a finally: block along with a try: block. The finally block is a place to put any code that must
execute, whether the try-block raised an exception or not. The syntax of the try-finally statement is this –
try:
Youdo your operations here;
......................
Due to any exception,this may be skipped.
finally:
This would always be executed.
......................
try:
fh=open("testfile","w")
fh.write("This is my test file for exception handling!!")
finally:
print"Error: can\'t find file or read data"
If you do not have permission to open the file in writing mode, then this will produce the following result –
Error: can't find file or read data
1. Write a Python class named Circle constructed by a radius and two methods which will compute the
area and the perimeter of a circle.
Sample Program:
classCircle():
def__init__(self, r):
self.radius= r
defarea(self):
returnself.radius**2*3.14
defperimeter(self):
return2*self.radius*3.14
NewCircle=Circle(8)
print(NewCircle.area())
3
2
print(NewCircle.perimeter())
Sample Output:
200.96
50.24
Flowchart:
Sample Program:
classpy_solution:
defreverse_words(self, s):
return' '.join(reversed(s.split()))
print(py_solution().reverse_words(‘hello .py’))
Sample Output:
.py hello
Pictorial Presentation
3
3
Flowchart:
3.Write a Python program to import built-in array module and display the namespace of the said
module.
Sample Program:
import array
for name inarray.__dict__:
print(name)
Sample Output:
__name__
__doc__
__package__
__loader__
__spec__
3
4
__array_reconstructor
ArrayType
array
typecodes
Sample Program:
class Vehicle:
def __init__(self, max_speed, mileage):
self.max_speed = max_speed
self.mileage = mileage
Sample Output:
240 18
5. Create a child class Bus that will inherit all of the variables and methods of the Vehicle class
Sample Program
class Vehicle:
class Bus(Vehicle):
pass
Sample Output:
Vehicle Name: School Volvo Speed: 180 Mileage: 12
EXERCISES
1. Write a Python class named Rectangle constructed by a length and width and a method which
will compute the area of a rectangle.
2. Write a Python class which has two methods get_String and print_String. get_Stringaccept a
3
5
string from the user and print_String print the string in upper case.
3. Create a Bus child class that inherits from the Vehicle class. The default fare charge of any
vehicle is seating capacity * 100. If Vehicle is Bus instance, we need to add an extra 10% on full
fare as a maintenance charge. So total fare for bus instance will become the final amount = total
fare + 10% of the total fare.
4. Write a Python class to implement pow(x, n).
5. Create two new vehicles called car1 and car2 that inherits from Vehicles class. Set car1 to be a red
convertible worth $60,000.00 with a name of Fer, and car2 to be a blue van named Jump worth
$10,000.00.
3
6