Object Oriented Programming Concept in Python

Rina Mondal
Python’s Gurus
Published in
10 min readMay 13, 2024
Photo by Michael Dziedzic on Unsplash

Object Oriented Programming (OOP) is centered around the concept of objects and classes. In OOP, you write classes that represent real-world things and situations, and you create objects based on these classes. When you write a class, you define the general behaviour that a whole category of objects can have. Each object is automatically equipped with the general behavior, you can then give each object whatever unique traits you desire.

Everything around us is an object and that always belongs to some particular class. Ex: If the vehicle is a class, then car, bike, and truck are all objects of the vehicle class. If the animal is a class, then dogs, cats, raccoons, and mink all may be objects of the animal class. When a dog is a class, then each type of dog may be the object of the dog class. When any type of dog like a bulldog is a class, then any particular bulldog may become its object.

Making an object from a class is called instantiation and you work with instances of a class.

In simple words:

Class- A class is a blueprint for creating objects. Object- An instance of a class.

Let’s do it Practically:

Now, we will first create a class and then discuss its components: First, write a dog class which can be any dog.

In general, we consider, that pet dogs have a name, age, and two behaviors- sit and act. These attributes will be present in our dog class because they are common to most dogs. After our class is written, we will use it to make individual instances, each of which represents one specific dog.

# Syntax of creating a class:
class classname: #class is the keyword used to create a class.
#create a constructor
#write attributes and methods
#Now, we will create a class named Dog and create some objects.

class Dog:
'''About Dog Class''' #This is a docstring which is not mandatory.
def __init__(self, name, age):
self.name=name
self.age=age

def sit(self):
print(f"{self.name} is now sitting")

def act(self):
print(f"{self.name} is now acting")

Now, a dog class is created. Name and age are the two attributes, and sit and act are the behaviors. Each object or instance can access those attributes and behaviors.

Let’s understand the difference between attributes and functions/behaviors:

Attributes are pieces of data that are associated with a class like a name, age, breed, or color. These are all attributes.

Functions are like performing something. It is like acting, dancing, playing, etc. It’s a process we can say.

Now, the __init__ method:

A class in Python can have a special method called a constructor. The name of the constructor method in Python is __init__. The constructor is called whenever an object of a class is created. It does not wait for the object to call the function. It is automatically executed when the object is created. This method will always contain two leading underscores and two trailing underscores. Here it contains 3 parameters: self, name, and age. Self is a reference to an object or you can imagine its the object itself. It gives the individual instance access to the attributes and methods in the class. When we make an instance of a dog, Python will call the __init__ method, and the self is passed automatically.

Here, I have added two more arguments: name, and age. You can add as many as you want. While creating an object you have to pass this argument. But, you do not need to pass the self-argument.

Methods vs Functions:

The functions written inside the class are known as methods. There is no other difference between methods and functions. Now, onwards we will call this by method.

Making an instance of a class:

You can create as many instances as you want. Here, we will create two instances.

dog1=Dog("Tom", 6)
print(my_dog.age)

dog2= Dog("Tommy", 3)
print(your_dog.name)

dog1 and dog2 are two instances of the Dog class. They can access the attributes of the dog class i.e. name and age.

Accessing the attributes:

The attributes can be accessed by providing class name.attributes.

print (f"Name of dog1 is"{dog1.name})
print (f"Name of dog1 is"{dog2.name})
print (f"Age of dog1 is"{dog1.age})
print (f"Age of dog1 is"{dog2.age})
#This way, you can access the attributes.

Calling Methods:

You can call the methods also by using the object.

dog1.sit()
dog2.act()
dog1.sit()
dog2.act()

Let’s create another class named Car. my_car is an object. my_car can access all the attributes of the Car class.

class Car:

def __init__(self, make, model, year):
self.make=make
self.model=model
self.year=year

def get_name(self):
long_name= f"{self.year} {self.make}{self.model}"
return long_name.title()

my_car= Car('audi','a2', 2019)
print(my_car.get_name())

Hence, A car class is created. my_car is an object of the class Car. It can access all the features of the class. Now, let’s see the things we can do with a class.

Setting a default value for an attribute:

Here, I will make the odometer_reading zero by default. I can write it in the __init__ method.

class Car:
def __init__(self, make, model, year):
self.make=make
self.model=model
self.year=year
self.odometer_reading=0 #Setting default value

def get_name(self):
long_name= f"{self.year} {self.make}{self.model}"
return long_name.title()

def read_odometer(self):
print(f"This car has {self.odometer_reading} miles on it")

my_car= Car('audi' , ' a2', 2019)
print(my_car.get_name())
my_car.read_odometer()

Modifying an Attribute’s value Directly:

Now, I want to modify the odometer_reading directly.

class Car:

'''des'''
def __init__(self, make, model, year):
self.make=make
self.model=model
self.year=year
self.odometer_reading=0

def get_name(self):
long_name= f"{self.year} {self.make}{self.model}"
return long_name.title()

def read_odometer(self):
print(f"This car has {self.odometer_reading} miles on it")

my_car= Car('audi', 'a2', 2019)
print(my_car.get_name())
my_car.odometer_reading= 23
my_car.read_odometer()

Modifying an attribute’s value through a method:

Instead of updating directly, I want to update this using a method. Hence, I will create a method update_odometer and pass the value as arguments.

class Car:
'''des'''
def __init__(self, make, model, year):
self.make=make
self.model=model
self.year=year
self.odometer_reading=0

def get_name(self):
long_name= f"{self.year} {self.make}{self.model}"
return long_name.title()

def read_odometer(self):
print(f"This car has {self.odometer_reading} miles on it")

def update_odometer(self, mileage):
self.odometer_reading=mileage

my_car= Car('audi' , ' a2', 2019)
print(my_car.get_name())
my_car.update_odometer(27)
my_car.read_odometer()

Incrementing an Attribute’s value through a method:

Now, I want to increment the value by using a method. See, how I do it.

class Car:
'''des'''
def __init__(self, make, model, year):
self.make=make
self.model=model
self.year=year
self.odometer_reading=0

def get_name(self):
long_name= f"{self.year} {self.make}{self.model}"
return long_name.title()

def read_odometer(self):
print(f"This car has {self.odometer_reading} miles on it")

def update_odometer(self, mileage):
self.odometer_reading=mileage

def increment_odometer(self, miles):
self.odometer_reading+=miles

my_car= Car('audi' , ' a2', 2019)
print(my_car.get_name())
my_car.increment_odometer(30)
my_car.read_odometer()

Now, let’s know about the main properties of OOPS:

  1. Encapsulation
  2. Inheritance
  3. Polymorphism
  4. Abstraction

Encapsulation:

Encapsulation ensures that the internal state of an object is hidden from the outside world. We achieve this in Python by using private attributes and methods, denoted by a leading double underscore (__).

class Car:
def __init__(self, make, model, year):
self.make = make # Public attribute
self.model = model # Public attribute
self.year = year # Public attribute
self.mileage = 0 # Public attribute

# Creating a Car object
my_car = Car("Toyota", "Camry", 2020)

# Directly accessing and modifying attributes (violation of encapsulation)
my_car.mileage = 10000
print("Updated Mileage:", my_car.mileage)
#By Using attributes directly, anyone can misuse them. To Protect, OOP provides encapsulation


class Car:
def __init__(self, make, model, year):
self.__make = make # Private attribute
self.__model = model # Private attribute
self.__year = year # Private attribute
self.__mileage = 0 # Private attribute
# Getter methods to access private attributes
def get_make(self):
return self.__make
def get_model(self):
return self.__model
def get_year(self):
return self.__year
def get_mileage(self):
return self.__mileage
# Setter method to update mileage
def add_mileage(self, miles):
if miles > 0:
self.__mileage += miles
# Creating a Car object
my_car = Car("Toyota", "Camry", 2020)

# Accessing attributes using getter methods
print("Make:", my_car.get_make())
print("Model:", my_car.get_model())
print("Year:", my_car.get_year())
print("Mileage:", my_car.get_mileage())

# Trying to access private attributes directly (will result in an AttributeError)
# print(my_car.make) # Uncommenting this line will result in an error
print(my_car. __make) #It can be accessed as nothing is truly private in Python but it is recommended not to access the private attributes directly.

# Updating mileage using setter method
my_car.add_mileage(100)
print("Updated Mileage:", my_car.get_mileage())

Python keeps private attributes hidden from the outside world. In simple words, the junior programmer or the user can not see the private variables present inside the class.

However, if they know, they can access it by double underscore. This is because nothing is truly private in Python, but it is recommended that private attributes must be accessed through a method only which is also known as getter methods and setter methods. Python encapsulates all the variables like private variables, public variables, and methods together and makes it secured.

Inheritance:

Inheritance allows a class to inherit attributes and methods from another class. Let’s extend the Car class to create a specific type of car, an electric car:

In OOP, there are four types of Inheritance:

  1. Single-level Inheritance: Refers to the scenario in object-oriented programming where a class inherits attributes and behaviors from only one parent class, forming a single level of inheritance hierarchy.
# Single Inheritance Example: Vehicle as superclass and Car as subclass
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
def display_info(self):
print(f"Make: {self.make}, Model: {self.model}")

class Car(Vehicle):
def __init__(self, make, model, color):
super().__init__(make, model)
self.color = color
def display_info(self):
super().display_info()

print(f"Color: {self.color}")

# Creating an object of subclass
my_car = Car("Toyota", "Camry", "Blue")
my_car.display_info()

2. Multiple Inheritance: A class can inherit attributes and behaviors from more than one parent class.

# Multiple Inheritance Example: Vehicle and ElectricDevice as superclasses for ElectricCar

class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
def display_info(self):
print(f"Make: {self.make}, Model: {self.model}")

class ElectricDevice:
def __init__(self, battery_capacity):
self.battery_capacity = battery_capacity
def display_battery_capacity(self):
print(f"Battery Capacity: {self.battery_capacity} kWh")

class ElectricCar(Vehicle, ElectricDevice):
def __init__(self, make, model, color, battery_capacity):
Vehicle.__init__(self, make, model)
ElectricDevice.__init__(self, battery_capacity)
self.color = color
def display_info(self):
super().display_info()
self.display_battery_capacity()
print(f"Color: {self.color}")

# Creating an object of subclass
my_electric_car = ElectricCar("Tesla", "Model S", "Red", 100)
my_electric_car.display_info()

3. Multilevel Inheritance: A concept in object-oriented programming where a subclass inherits from another subclass, creating a hierarchy of inheritance with multiple levels.

# Multilevel Inheritance Example: Vehicle as a superclass, Car as an intermediate subclass, and Sedan as a subclass

class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
def display_info(self):
print(f"Make: {self.make}, Model: {self.model}")

class Car(Vehicle):
def __init__(self, make, model, color):
super().__init__(make, model)
self.color = color
def display_info(self):
super().display_info()
print(f"Color: {self.color}")

class Sedan(Car):
def __init__(self, make, model, color, num_doors):
super().__init__(make, model, color)
self.num_doors = num_doors
def display_info(self):
super().display_info()
print(f"Number of Doors: {self.num_doors}")

# Creating an object of a subclass
my_sedan = Sedan("Honda", "Accord", "Black", 4)
my_sedan.display_info()

4. Hierarchical Inheritance: When classes are organized in a tree-like structure, where child classes inherit attributes and behaviors from parent classes, creating a hierarchy of relationships.

# Hierarchical Inheritance Example: Vehicle as superclass, Car and Truck as subclasses
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model

def display_info(self):
print(f"Make: {self.make}, Model: {self.model}")

class Car(Vehicle):
def __init__(self, make, model, color):
super().__init__(make, model)
self.color = color

def display_info(self):
super().display_info()
print(f"Color: {self.color}")

class Truck(Vehicle):
def __init__(self, make, model, color, payload_capacity):
super().__init__(make, model)
self.color = color
self.payload_capacity = payload_capacity

def display_info(self):
super().display_info()
print(f"Color: {self.color}")
print(f"Payload Capacity: {self.payload_capacity} tons")

# Creating objects of subclasses
my_truck = Truck("Ford", "F-150", "White", 3)
my_truck.display_info()
my_car = Car("Toyota", "Corolla", "Silver")
my_car.display_info()

Polymorphism:

Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows objects to be treated as instances of their superclass, enabling different objects to respond to the same message (method call) in different ways. It promotes code flexibility, extensibility, and reuse by allowing methods to behave differently based on the object they are invoked upon. Polymorphism is typically achieved through method overriding and method overloading.

Method Overriding: Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass. When a method is called on an object of the subclass, the subclass method is invoked instead of the superclass method i.e. subclass methods override the same method of the parent class.

class Animal:
def speak(self):
print("Animal speaks")

class Dog(Animal):
def speak(self): # Method overridden in subclass
print("Dog barks")

class Cat(Animal):
def speak(self): # Method overridden in subclass
print("Cat meows")

# Polymorphic behavior
animals = [Dog(), Cat()]
for an animal in animals:
animal. speak() # Output depends on the type of animal

Method Overloading: Method overloading allows a class to define multiple methods with the same name but with different parameters. The appropriate method is selected based on the number and types of arguments passed during the method call. Though Python does not support method overloading. This is available in other Object Oriented Programming language.

class Calculator:
def add(self, a, b):
return a + b
def add(self, a, b, c):
return a + b + c

# Method overloading
calc = Calculator()
print(calc.add(2, 3))
# Output: Error! No method with two parameters.
print(calc.add(2, 3, 4))
# Output: 9

In this example, the calculator class defines two add methods with different numbers of parameters. Depending on the number of arguments passed during the method call, the appropriate add method is invoked, demonstrating method overloading. That was our attempt to do but we could not succeed because Python does not support method overloading. If we try to achieve it will take the latest written method as the only one and show the error accordingly.

Polymorphism enables code to be written in a way that is more generic, flexible, and easier to maintain. It allows different implementations to coexist and be used interchangeably, enhancing code reusability and extensibility.

Abstraction:

A class is considered an abstract class if it contains one or more abstract methods. An abstract method is a method declared in the abstract class but lacks an implementation. Subclasses inheriting from an abstract class must provide concrete implementations for all abstract methods defined by the abstract class. If a subclass also declares abstract methods without implementation, it too must be declared abstract, ensuring that concrete subclasses implement necessary functionality.

This feature is mainly for imposing any restrictions or we can say imposing a condition on the child’s class.

Let’s illustrate abstraction with a Python code example using abstract classes:

from abc import ABC, abstractmethod # We have to import a package named ABC
# which is a module that provides the infrastructure for defining abstract base classes (ABCs) in Python.

class Shape(ABC):
def __init__(self, name):
self.name = name
@abstractmethod
def area(self):
pass

class Rectangle(Shape):
def __init__(self, name, length, width):
super().__init__(name)
self.length = length
self.width = width
def area(self):
return self.length * self.width

class Circle(Shape):
def __init__(self, name, radius):
super().__init__(name)
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius

rectangle = Rectangle("Rectangle", 5, 3)
circle = Circle("Circle", 7)
print(f"{rectangle.name} Area:", rectangle.area())
print(f"{circle.name} Area:", circle.area())

Explore Data Science Roadmap.

Complete Explanations of OOPs in my YouTube channel.

Complete Python course required for Data Science in a single video.

Hence, we have discussed everything related to the OOPs Concept.

Give it :👏👏👏👏:

If you found this guide helpful, why not show some love? and if you have questions or topics you’d like to explore further, drop a comment 💬 below 👇

Python’s Gurus🚀

Thank you for being a part of the Python’s Gurus community!

Before you go:

  • Be sure to clap x50 time and follow the writer ️👏️️
  • Follow us: Newsletter
  • Do you aspire to become a Guru too? Submit your best article or draft to reach our audience.

--

--

Rina Mondal
Python’s Gurus

I have an 8 years of experience and I always enjoyed writing articles. If you appreciate my hard work, please follow me, then only I can continue my passion.