Object Oriented Programming in Python : Learn by Examples

Deepanshu Bhalla 19 Comments
This tutorial outlines object oriented programming (OOP) in Python with examples. It is a step by step guide which was designed for people who have no programming experience. Object Oriented Programming is popular and available in other programming languages besides Python which are Java, C++, PHP.

What is Object Oriented Programming?

In object-oriented programming (OOP), you have the flexibility to represent real-world objects like car, animal, person, ATM etc. in your code. In simple words, an object is something that possess some characteristics and can perform certain functions. For example, car is an object and can perform functions like start, stop, drive and brake. These are the function of a car. And the characteristics are color of car, mileage, maximum speed, model year etc.

In the above example, car is an object. Functions are called methods in OOP world. Characteristics are attributes (properties). Technically attributes are variables or values related to the state of the object whereas methods are functions which have an effect on the attributes of the object.

In Python, everything is an object. Strings, Integers, Float, lists, dictionaries, functions, modules etc are all objects.
OOP Python

Do Data Scientists Use Object Oriented Programming?

It's one of the most common question data scientists have before learning OOP. When it comes to data manipulation and machine learning using Python, it is generally advised to study pandas, numpy, matplotlib, scikit-learn libraries. These libraries were written by experienced python developers to automate or simplify most of tasks related to data science. All these libraries depend on OOP and its concepts. For example, you are building a regression model using scikit-learn library. You first have to declare your model as an object and then you use a fit method. Without knowing fundamentals of OOP, you would not be able to understand why you write the code in this manner.

In python, there are mainly 3 programming styles which are Object-Oriented Programming, Functional Programming and Procedural Programming. In simple words, there are 3 different ways to solve the problem in Python. Functional programming is most popular among data scientists as it has performance advantage. OOP is useful when you work with large codebases and code maintainability is very important.

Conclusion : It's good to learn fundamentals of OOP so that you understand what's going behind the libraries you use. If you aim to be a great python developer and want to build Python library, you need to learn OOP (Must!). At the same time there are many data scientists who are unaware of OOP concepts and still excel in their job.

Basics : OOP in Python

In this section, we will see concepts related to OOP in Python in detail.

Object and Class

Class is a architecture of the object. It is a proper description of the attributes and methods of a class. For example, design of a car of same type is a class. You can create many objects from a class. Like you can make many cars of the same type from a design of car.

Class Methods Attributes

There are many real-world examples of classes as explained below -

  • Recipe of Omelette is a class. Omelette is an object.
  • Bank Account Holder is a class. Attributes are First Name, Last Name, Date of Birth, Profession, Address etc. Methods can be "Change of address", "Change of Profession", " Change of last name" etc. "Change of last name" is generally applicable to women when they change their last name after marriage
  • Dog is a class. Attributes are Breed, Number of legs, Size, Age, Color etc. Methods can be Eat, Sleep, Sit, Bark, Run etc.

In python, we can create a class using the keyword class. Method of class can be defined by keyword def. It is similar to a normal function but it is defined within a class and is a function of class. The first parameter in the definition of a method is always self and method is called without the parameter self.

Example 1 : Create Car Class
  • class : car
  • attributes : year, mpg and speed
  • methods : accelerate and brake
  • object : car1
class car:
    
    # attributes
        year = 2016     # car model's year
        mpg =  20       # mileage
        speed = 100     # current speed
        
    # methods
        def accelerate(self):
            return car.speed + 20

        def brake(self):
            return car.speed - 50
car1=car()

car1.accelerate()
120

car1.brake()
50

car1.year
2016

car1.mpg
20

car1.speed
100
To submit methods, we need to use rounded brackets.
Example 2 : Create Company Class
In the example below, we are creating a class called company. Here attributes are name, turnover, revenue and number of employees working in the company. Method is revenue generated per employee (let's call it productivity for demonstration purpose).
# Creates class Company
class Company:
    
    # attributes
    name = "XYZ Bank"
    turnover = 5000
    revenue = 1000
    no_of_employees = 100
    
    # method
    def productivity(self):
        return Company.revenue/Company.no_of_employees

Attributes which are defined outside of method can be extracted without creating object.

Company.name

Output
'XYZ Bank'

Company.turnover

Output
5000

Company.no_of_employees

Output
100

Company().productivity()

Output
10.0

Constructor

Constructor is a special method. You can think of as a function which initializes or activates the attributes or properties of the class for a object. Use keyword __init__ to create a method for constructor. In the above section we discussed an example of car as an object. You can think of constructor as entire sequence of actions required so that the factory constructs a car object out of the class design pattern. self represents that object which inherits those properties.

Objects are instances of a class. Words 'instance' and 'object' are used interchangeably. The process of creating an object of a class is called instantiation.

In the following example, we are asking user to input values. __init__ is called when ever an object of the class is constructed.

class person:
        def __init__(self,firstname,lastname):
            self.first = firstname
            self.last = lastname

myname = person("Deepanshu","Bhalla")
print(myname.last)
We have created myname object of person class.
When you create a new object >>> __init__ method is called  >>> Behavior within the__init__ method executes

Let's take another example. Here, the program below returns output based on the method defined in class

class MyCompany:
        
    # methods
    def __init__(self, compname, revenue, employeesize):
        self.name = compname
        self.revenue = revenue
        self.no_of_employees = employeesize

    def productivity(self):
        return self.revenue/self.no_of_employees

MyCompany('XYZ Bank', 1000,100).productivity()

Output
10.0

MyCompany('ABC Bank', 5000,200).productivity()

Output
25.0
Alternative way of calling the class method
Bank = MyCompany('ABC Bank', 5000,200)
MyCompany.productivity(Bank)

Variables

Attributes of a class are also referred to as variables. There are two kind of variables - one which is declared inside a class but outside of methods of class and the other one which is declared inside __init__.

When you use __int__ method, you can access variables only after you create a object. These variables are called Instance Variables or local variables. One which is defined outside of methods are called Class Variables or global variables. You can access these variables anywhere in the class. See the difference in the example below.

class MyCompany:
    #Class Variable
    growth = 0.1
            
    def __init__(self, compname, revenue, employeesize):
        #Instance Variables
        self.name = compname
        self.revenue = revenue
        self.no_of_employees = employeesize

MyCompany.growth
0.1
How to get revenue variable from MyCompany class?
Wrong Way
MyCompany.revenue
AttributeError: type object 'MyCompany' has no attribute 'revenue'
Correct Way
Bank = MyCompany('DBA Bank',50000, 1000)
Bank.revenue
50000
MyCompany.revenue returns error as it cannot be accessed because object has not been created.

Methods

In python, there are three types of methods which are Instance, Class and Static.
  • Instance takes self as the first argument. They are also called Object or regular method. It's the same method which we have learnt so far in previous sections.
  • Class takes cls as the first argument. cls refers to class. To access a class variable within a method, we use the @classmethod decorator, and pass the class to the method
  • Staticdoesn't take anything as the first argument. It has limited uses which are explained in the latter part of this article.
How Instance and class methods are different?
Instance method can access properties unique to a object or instance. Whereas Class method is used when you want to access a property of a class, and not the property of a specific instance of that class. The other difference in terms of writing style is that Instance method take self as a first parameter whereas Class method takes cls as a first parameter.

In the example below, we are creating class for cab. Both Cab and taxi means the same thing. Attributes or properties of cab is driver name, number of kilometers run by a cab, Pick-up and drop location, cab fare and number of passengers boarded cab.

Here we are creating 3 methods : rateperkm, noofcabs, avgnoofpassengers. First one is instance method and other two are class methods.

  • rateperkm returns price of cab fare per km which is calculated by dividing total bill by no. of kms cab traveled.
  • noofcabs returns number of cabs running. Think about cab agency which owns many cabs and wants to know how many cabs are busy
  • avgnoofpassengers returns average number of passengers traveling in a car. To calculate average, it takes into account all the cabs running and number of passengers in each cab.
class Cab:
    
    #Initialise variables for first iteration
    numberofcabs  = 0
    numpassengers = 0

    def __init__(self,driver,kms,places,pay,passengers):
        self.driver = driver
        self.running = kms
        self.places = places
        self.bill = pay
        Cab.numberofcabs  =  Cab.numberofcabs + 1
        Cab.numpassengers =  Cab.numpassengers + passengers

    #Returns price of cab fare per km
    def rateperkm(self):
        return self.bill/self.running
        
    #Returns number of cabs running         
    @classmethod
    def noofcabs(cls):
        return cls.numberofcabs

    #Returns average number of passengers travelling in a cab
    @classmethod
    def avgnoofpassengers(cls):
        return int(cls.numpassengers/cls.numberofcabs)

firstcab  = Cab("Ramesh", 80, ['Delhi', 'Noida'], 2200, 3)
secondcab = Cab("Suresh", 60, ['Gurgaon', 'Noida'], 1500, 1)
thirdcab  = Cab("Dave", 20, ['Gurgaon', 'Noida'], 680, 2)

firstcab.driver
'Ramesh'

secondcab.driver
'Suresh'

thirdcab.driver
'Dave'
firstcab.rateperkm()
27.5

secondcab.rateperkm()
25.0

thirdcab.rateperkm()
34.0
Cab.noofcabs()
3


Cab.avgnoofpassengers()
2
Cab.avgnoofpassengers() returns 2 which is calculated by (3 + 1 + 2) / 3
Static Methods
Static method is the least popular method among all the three methods. Unlike instance and class methods, static method does not take a special keyword (self, cls) as a first parameter. It has a limited use because Neither you can access to the properties of an instance (object) of a class NOR you can access to the attributes of the class. The only usage is it can be called without an object. It is mainly useful for creating helper or utility functions like validation of driver name (driver name must be less than 32 characters) or bill amount must be more than zero (can't be negative or zero). See the program below for the same task.
class Cab:
    
    @staticmethod
    def billvalidation(pay):
        return int(pay) > 0

Cab.billvalidation(0.2)

Output
False

Inheritance

Inheritance makes use of code for Children class that has been already written for Parent class. For example, some attributes of vehicle class is same as car, bus and truck class. Driver Name, Number of Wheels etc. attributes are same in all the classes. Vehicle is a parent class and Car, bus and truck are children classes. In OOO, it means a class inherits attributes and behavior methods from its parent class.
  • Create a parent class Vehicle and using its attributes for child class Vehicle. In the program below, we don't need to specify attributes of class cab. It inherits from vehicle.
  • class Vehicle:
        def __init__(self,driver,wheels,seats):
            self.driver = driver
            self.noofwheels = wheels
            self.noofseats = seats
    
    class Cab(Vehicle):
        pass
    
    cab_1 = Cab('Sandy',4, 2)
    cab_1.driver
    
    Output
    'Sandy'
  • How to change class variable of subclass Vehicle
  • class Vehicle:
        minimumrate = 50
        def __init__(self,driver,wheels,seats):
            self.driver = driver
            self.noofwheels = wheels
            self.noofseats = seats
    
    class Cab(Vehicle):
        minimumrate = 75
    
    Vehicle.minimumrate
    50
    
    Cab.minimumrate
    75
  • How to have child class with more parameters than our parent class
  • In this example, we have two classes Cab and Bus which have many attributes which are similar but there are a few which are unique to class. To solve this, we have created a parent class named Vehicle which contains common attributes and method.
    inheritance python
    class Vehicle:
        minimumrate = 50
        def __init__(self,driver,wheels,seats,kms,bill):
            self.driver = driver
            self.noofwheels = wheels
            self.noofseats = seats
            self.running = kms
            self.bill = bill
        
        def rateperkm(self):
            return self.bill/self.running
    
    class Cab(Vehicle):
        minimumrate = 75    
        def __init__(self,driver,wheels,seats,kms,bill,cabtype):
            Vehicle.__init__(self,driver,wheels,seats,kms,bill)
            self.category = cabtype
    
    
    class Bus(Vehicle):
        minimumrate = 25 
        def __init__(self,driver,wheels,seats,kms,bill,color):
            Vehicle.__init__(self,driver,wheels,seats,kms,bill)
            self.color = color
    
    cab_1 = Cab('Prateek', 4, 3, 50, 700, 'SUV')
    cab_1.category
    cab_1.rateperkm()
    
    bus_1 = Bus('Dave', 4, 10, 50, 400, 'green')
    bus_1.color
    bus_1.rateperkm()
We can replace this command Vehicle.__init__(self,driver,wheels,seats,kms,bill) with super().__init__(driver,wheels,seats,kms,bill).
super() is used to refer the parent attributes and methods.

Polymorphism

Polymorphism means the ability to take various forms. It is an important concept when you deal with child and parent class. Polymorphism in python is applied through method overriding and method overloading.

Method Overriding

Method overriding allows us to have a method in the child class with the same name as in the parent class but the definition of the child class method is different from parent class method.
class Vehicle:
    def message(self):
        print("Parent class method")

class Cab(Vehicle):
    def message(self):
        print("Child Cab class method")

class Bus(Vehicle):
    def message(self):
        print("Child Bus class method")


x = Vehicle()
x.message()
Parent class method

y= Cab()
y.message()
Child Cab class method

z = Bus()
z.message()
Child Bus class method
As you can see the output shown above, children classes override the parent class method.

Method Overloading

It allows you to define a function or method with flexibility so that you can call it with only some of the arguments and no need to specify the other arguments. You can also call it with all the arguments. You can do it whatever the way you want.

In the script below, method can be called with no parameter (ignoring phrase parameter). Or it can be called with parameter phrase.

class Message:

    def details(self, phrase=None):
    
        if phrase is not None:
            print('My message - ' + phrase)
        else:
            print('Welcome to Python World')
        

# Object
x = Message()
    
# Call the method with no parameter
x.details()
    
# Call the method with a parameter
x.details('Life is beautiful')

What is __str__?

It is used to produce readable representation of the object.
class Vehicle:
    def __init__(self,driver,wheels,seats):
        self.driver = driver
        self.noofwheels = wheels
        self.noofseats = seats

veh_1 = Vehicle("Sandy", 4, 2)
print(veh_1)

Output
 __main__.Vehicle object at 0x0000019ECCCA05F8
class Vehicle:
    def __init__(self,driver,wheels,seats):
        self.driver = driver
        self.noofwheels = wheels
        self.noofseats = seats

    def __str__(self):
        return "Driver Name : " + self.driver + " ; " + "Number of seats in cab : " + str(self.noofseats)

veh_1 = Vehicle("Sandy", 4, 2)
print(veh_1)

Output
Driver Name : Sandy ; 
Number of seats in cab : 2

Data Encapsulation

Encapsulation of Data means restricting access to methods and variables. This can prevent the data from being modified by accident (mistake).
  • When we use two underscores '__' before attribute name, it makes attribute not accessible outside class. It becomes private attribute which means you can't read and write to those attributes except inside of the class. It is generally used by the developer of the module.
  • When you don't use underscore before attribute, it is a public attribute which can be accessed inside or outside of a class.
class Flat:
    def __init__(self):
        self.type = "premium"
        self.__bhk = "3 BHK"

flat_1 = Flat()
flat_1.type
premium

flat_1.__bhk
AttributeError: 'Flat' object has no attribute '__bhk'
In the above program, type is a public attribute and bhk is a private attribute which can't be accessed outside class.

Getters and Setters

They are used for retrieving and updating value of a variable. Setter is a method that updates value of a variable. Getter is a method that reads value of a variable. Let's learn it by examples.
class Vehicle:
    def __init__(self,driver_firstname,driver_lastname):
        self.fdriver = driver_firstname
        self.ldriver = driver_lastname
        self.email  = self.fdriver + '.' + self.ldriver + '@uber.com'

veh_1 = Vehicle("Sandy", "Stewart")

veh_1.fdriver
Sandy

veh_1.email
'Sandy.Stewart@uber.com'
Here we are updating driver's first name but it does not have an impact on email address which is a combination of first and last name.
veh_1.fdriver = 'Tom'
veh_1.fdriver
'Tom'

veh_1.email
'Sandy.Stewart@uber.com'
First name has been changed from Sandy to Tom but Email address remains same. Okay, the obvious question arises "how to update email address as well?". With the use of @property decorator we can change the behavior of email. email(self) is a method but it operates like a normal property. This special method is called Getters and Setters
class Vehicle:
    def __init__(self,driver_firstname,driver_lastname):
        self.fdriver = driver_firstname
        self.ldriver = driver_lastname
        
    @property  
    def email(self):
        return self.fdriver + '.' + self.ldriver + '@uber.com'

veh_1 = Vehicle("Sandy", "Stewart")
veh_1.fdriver = 'Tom'
veh_1.email
'Tom.Stewart@uber.com'
How to update first and last name automatically by changing email address
class Vehicle:
    def __init__(self,driver_firstname,driver_lastname):
        self.fdriver = driver_firstname
        self.ldriver = driver_lastname
        
    @property
    def email(self):
        return self.fdriver + '.' + self.ldriver + '@uber.com'

    @email.setter
    def email(self, address):
        first = address[:address.find('.')]
        last  = address[address.find('.')+1:address.find('@')]
        self.fdriver = first
        self.ldriver = last

veh_1 = Vehicle("Sandy", "Stewart")
veh_1.email = 'deep.bhalla@uber.com'
veh_1.fdriver
'deep'

veh_1.ldriver
'bhalla'
Validation
In real world, getters & setters are mainly used for including validation logic. In the example below, we are creating donation class with amount attribute. Amount must lie between 10 and 1,000,000. If user enters less than 10, it should be set as 10. Similarly, if user tries to enter a value greater than 1 million, it should be capped to 1 million only.
class donation:
    def __init__(self,amount):
        self.amount = amount
        
    @property
    def amount(self):
        return self.__amount

    @amount.setter
    def amount(self, amount):
        if amount < 10:
            self.__amount = 10
        elif amount > 1000000:
            self.__amount = 1000000
        else:
            self.__amount = amount


charity = donation(5)
charity.amount
10

How to import class

In this section, we will cover how to load class from different file or directory.
  1. Save the following script as Mymodule.py
  2. """
    Car Class
    """
    
    class Cab:
        
        #Initialise for first iteration
        numberofcabs  = 0
    
        def __init__(self,driver,kms,pay):
            self.driver = driver
            self.running = kms
            self.bill = pay
            Cab.numberofcabs  =  Cab.numberofcabs + 1
    
        #Returns average price per km
        def rateperkm(self):
            return self.bill/self.running
            
        #Returns number of cabs running         
        @classmethod
        def noofcabs(cls):
            return cls.numberofcabs
    
    
    if __name__ == "__main__":
        
        #Cab class
        firstcab  = Cab("Ramesh", 80, 1200)
    
        #driver attribute in Cab class
        print(firstcab.driver)
        
        #class method
        print(Cab.noofcabs())
  3. In the code below, specify directory where Mymodule.py file is stored
  4. import os
    os.chdir("C:/Users/DELL/Desktop/")
    import Mymodule
  5. Create object or run methods like we normally do. Make sure to add module name as prefix before using class and class method
  6. #Cab class in Mymodule.py
    firstcab  = Mymodule.Cab("Ramesh", 80, 1200)
    
    #driver attribute in cab class
    firstcab.driver
    
    #rateperkm instance method in Mymodule2.py
    firstcab.rateperkm()
    
    #noofcabs classmethod in Mymodule2.py
    Mymodule.Cab.noofcabs()
    To avoid writing module name to access class, you can use "from" which loads module into the current namespace.
    from Mymodule import *
    firstcab  = Cab("Sandy", 80, ['Delhi', 'Noida'], 1200, 3)

What is __name__ == "__main__"?

Any code that is inside if __name__ == '__main__': will be executed when you run your .py file directly (from terminal).

If you import module import Mymodule, the code inside if __name__ == '__main__': won't be run.

To test this, save and run the following script from terminal as well as accessing it using import command.
if __name__ == '__main__':
    print('First Result')
else:
    print('Second Result')
How to change directory in command prompt
Type cd followed by space and then folder name. Once right directory is set, you can enter name of your python script file. See the snapshot below.
run python script from terminal

Exercise

Create class Rectangle which has attributes length and width. Create 2 methods for calculating area of rectangle and perimeter of rectangle. Area is calculated by multiplying length by width. Perimeter is 2 times of (length + width). Solve and post your solution in the comment box.
Conclusion
After completion of this tutorial, you must have got a good idea of important topics of object oriented programming used in Python. Your next step should be practice the concepts you have learnt. Try to use it in your live projects.
Related Posts
Spread the Word!
Share
About Author:
Deepanshu Bhalla

Deepanshu founded ListenData with a simple objective - Make analytics easy to understand and follow. He has over 10 years of experience in data science. During his tenure, he worked with global clients in various domains like Banking, Insurance, Private Equity, Telecom and HR.

Post Comment 19 Responses to "Object Oriented Programming in Python : Learn by Examples"
  1. Well explained in details waiting for more

    ReplyDelete
  2. class Rectangle:
    def __init__(self, length, width):
    self.length = length
    self.width = width

    def get_area(self):
    return self.length * self.width

    def get_perimeter(self):
    return 2 * (self.length + self.width)


    r1 = Rectangle(10, 20)
    print(r1.get_area())
    print(r1.get_perimeter())

    ReplyDelete
  3. Succinctly written! This certainly is a solid boot-camp course for a Noob like me in the wonderful world of python programming...

    ReplyDelete
  4. class Rectangle:

    # Todo: has attributes length and width
    def __init__(self, length, width):
    self.length = length
    self.width = width

    @property
    def length(self):
    return self.__length

    @property
    def width(self):
    return self.__width

    @length.setter
    def length(self, length):
    if length <= 0:
    self.__length = -length + 1
    else:
    self.__length = length

    @width.setter
    def width(self, width):
    if width <= 0:
    self.__width = -width + 1
    else:
    self.__width = width

    # Todo: calculating area of rectangle
    def cal_area(self):
    return self.__length * self.__width

    # Todo: calculating perimeter of rectangle
    def cal_perimeter(self):
    return 2 * (self.__length + self.__width)


    if __name__ == '__main__':
    rec_1 = Rectangle(-10, -5)
    print("Rectangle 1")
    print(rec_1.length, "*", rec_1.width)

    rec_2 = Rectangle(10, 5)
    area = rec_2.cal_area()
    perimeter = rec_2.cal_perimeter()
    print("Rectangle 1")
    print(rec_2.length, "*", rec_2.width)
    print("Area:", area)
    print("perimeter:", perimeter)

    ReplyDelete
  5. This is really helpful. Thank you

    ReplyDelete
  6. Consider School_bus, bus, Vehicle class, with max_speed and mileage
    instance attributes, seeting_capacity and print bus_fees for the school
    student, if 1st to 10th standard, Rs.1000 per month and if 11th and 12th
    standard, Rs. 1500 per month. Write a pthon program using inheritance.

    ReplyDelete
  7. In the empty space below, write the necessary codes to :
    • Create a class named « cars ».
    • The class will contain the following public properties:
    model, year and color.
    • The class must contain a function
    made to output to screen the three properties.
    • Create a new object (instance) from the class you just created.



    answer ?

    ReplyDelete
  8. class Rectangle:
    def __init__(self, length, breadth):
    self.length = length
    self.breadth = breadth
    def cal_Area(self):
    return self.length * self.breadth

    def cal_Perimeter(self):
    return 2*(self.length + self.breadth)

    rect1 = Rectangle(5,4)
    rect1.cal_Area()

    ReplyDelete
  9. amazing well explained really helpful for me to understand OOPs concepts in detail

    ReplyDelete
  10. I can't express my words how well it is useful for me. I have my interview tmr on OOPS thank u for this

    ReplyDelete
  11. class Car:

    # class attribute
    wheels = 4

    # initializer / instance attributes
    def __init__(self, color, style):
    self.color = color
    self.style = style

    # method 1
    def showDescription(self):
    print("This car is a", self.color, self.style)

    # method 2
    def changeColor(self, color):
    self.color = color

    c = Car('Black', 'Sedan')

    # call method 1
    c.showDescription()
    # Prints This car is a Black Sedan

    # call method 2 and set color
    c.changeColor('White')

    c.showDescription()
    # Prints This car is a White Sedan

    ReplyDelete
  12. class Rectangle:
    def __init__(self, length, width):
    self.length = length
    self.width = width

    def area_rectangle(self):
    print("Area of the rectangle is", self.length*self.width)

    def perimeter(self):
    print("The Perimeter of the rectangle is", 2*(self.length + self.width))

    obj1=Rectangle(10,15)
    obj1.area_rectangle()
    obj1.perimeter()

    ReplyDelete
  13. class Rectangle:
    def __init__(self,length,width):
    self.length = length
    self.width = width

    def area(self):
    return self.length * self.width

    def perimeter(self):
    return 2*(self.length + self.width)

    r1 = Rectangle(2,4)
    print (r1.area())
    print (r1.perimeter())

    ReplyDelete
Next → ← Prev