Skip to main content

Python OOPs Concepts

This page provides an introduction to python oops concept.

Class

# create a class
class Car:

# class variables are shared among all the instances of class
wheel = 4

# constructor
def __init__(self, model, year, color, for_sale):
# instance variables
self.model = model
self.year = year
self.color = color
self.for_sale = for_sale


def drive(self):
print(f"You drive {self.model}")


# create a object
car_1 = Car("Range Rover", 2024, "white", False)

# printing object
print(car_1)

# printing object attributes
print(car_1.model)
# Range Rover

print(car_1.year)
# 2024

print(car_1.drive())
# You drive Range Rover

Inheritance

class Animal:
# constructor
def __init__(self, name):
self.name = name
self.is_alive = True

# eat function
def eat(self):
print(f"{self.name} is eating")


# inheritance
class Dog(Animal):
pass


# inheritance
class Cat(Animal):
pass


# inheritance
class Mouse(Animal):
pass


# creating objects
dog = Dog("Scooby")
cat = Cat("Garfield")
mouse = Mouse("Mickey")


# printing object attributes
print(dog.name)
print(dog.is_alive)


# calling eat object from parent class Animal
dog.eat()

Multiple Inheritance

# parent's parent class 
class Animal:
def __init__(self, name):
self.name = name

def eat(self):
print(f"{self.name} is eating")

def sleep(self):
print(f"{self.name} is sleeping")



# parent class
class Pray(Animal):
def flee(self):
print(f"{self.name} is fleeing")


# parent class
class Predator(Animal):
def hunt(self):
print(f"{self.name} is hunting")


# child class - multiple inheritance
class Fish(Pray, Predator):
pass


# fish object inherits methods from both Pray and Predator class
fish = Fish("Fish")


fish.hunt()
# o/p: Fish is hunting
fish.flee()
# o/p: Fish is fleeing


# fish object can also inherits method from animal class - parent of Pray and Predator
fish.eat()
# o/p: Fish is eating
fish.sleep()
# o/p: Fish is sleeping

Abstract Classes

from abc import ABC, abstractmethod

class Vehicle(ABC):
@abstractmethod
def go(self):
pass

@abstractmethod
def stop(self):
pass


# vehicle = Vehicle()
# o/p: TypeError: Can't instantiate abstract class Vehicle
# without an implementation for abstract


class Car(Vehicle):
def go(sefl):
print("You drive a car")

def stop(self):
print("You stop a car")


# now you can instantiate an object Car class
# which is child of Vehicle abstract class
car = Car()

# calling go and stop objects on Car objects.
car.go()
# o/p: You drive a car

car.stop()
# o/p: You stop a car
info

For creating an abstract class, you need to import ABC and abstractmethod from abc package. You can't create an objects of abstract class directly.

Super

# parent class
class Shape:
def __init__(self, color, is_filled):
self.color = color
self.is_filled = is_filled


# child class
class Circle(Shape):
def __init__(self, color, is_filled, redius):
super().__init__(color, is_filled)
self.redius = redius


# child class
class Square(Shape):
def __init__(self, color, is_filled, width):
super().__init__(color, is_filled)
self.width = width


# creating objects
circle = Circle(color="red", is_filled=True, redius=5)
print(circle.color)
# o/p: red


square = Square(color="yellow", is_filled=False, width=5)
print(square.color)
# o/p: yellow
print(square.is_filled)
# o/p: False

Polymorphism

# import ABC, abstractmethod for abstract class
from abc import ABC, abstractmethod

# parent class
class Shape:
@abstractmethod
def area(self):
pass


# child class
class Circle:
def __init__(self, radius):
self.radius = radius

def area(self):
return 3.14 * self.radius ** 2


# child class
class Square:
def __init__(self, side):
self.side = side

def area(self):
return self.side ** 2


# generating shapes
shapes = [Circle(4), Square(5)]
for shape in shapes:
print(f"{shape.area()} cm^2")
# o/p
# 50.24 cm^2
# 25 cm^2

Aggregation

It is a relationship where one object contains references to one or more independent objects.

# library contains objects of type book
class Library:
def __init__(self, name):
self.name = name
self.books = []

def add_book(self, book):
self.books.append(book)

def list_books(self):
return [f"{book.title} by {book.author}" for book in self.books]


class Book:
def __init__(self, title, author):
self.title = title
self.author = author


library = Library("New York Public Library")
book_1 = Book("Harry Potter", "J. K. Rowling")
book_2 = Book("The Hobbit", "J. R. R. Tolkien")

library.add_book(book_1)
library.add_book(book_2)

print(library.name)
# o/p: New York Public Library

print(library.list_books())
# o/p: ['Harry Potter by J. K. Rowling', 'The Hobbit by J. R. R. Tolkien']

Composition

The composed object directly owns its components, which cannot exits independently "owns" relationship.

class Engine:
def __init__(self, horse_power):
self.horse_power = horse_power


class Wheel:
def __init__(self, size):
self.size = size


# car class "owns" an engine and wheel
class Car:
def __init__(self, make, model, horse_power, wheel_size):
self.make = make
self.mode = model
self.engine = Engine(horse_power)
self.wheels = [Wheel(wheel_size) for wheel in range(4)]


# objects creation
car = Car(make="Range Rover", model="4", horse_power=500, wheel_size=18)

car.make
# o/p: Range Rover

Nested Class

# outer class
class Company:
# inner class
class Employee:
def __init__(self, name, position):
self.name = name
self.position = position

def __str__(self):
return f"{self.name}"

def __init__(self, company_name):
self.company_name = company_name
self.employees = []

# using Employee inner class using self.
def add_employee(self, name, position):
new_employee = self.Employee(name, position)
self.employees.append(new_employee)


# object creation
company = Company("Brocoli")
company.add_employee("Spongbob", "cook")
company.add_employee("Spongbob", "cashier")

# printing all employees of company
print("\n".join(str(employee) for employee in company.employees))

Static Methods

Static method are general utility method that doesn't need access to class data.

class Employee:
@staticmethod
def is_valid_position(position):
valid_positions = ["Manager", "Cashier", "Cook", "Janitor"]
return position in valid_positions

print(Employee.is_valid_position("Cook"))
# o/p: True

Class Method

Allow operation related to class itself.

class Student:
count = 0
total_gpa = 0

def __init__(self, name, gpa):
self.name = name
self.gpa = gpa
Student.count += 1
Student.total_gpa += gpa

# instance method
def get_info(self):
return f"{self.name} {self.gpa}"

@classmethod
def get_count(cls):
return f"Total # of students: {cls.count}"

@classmethod
def get_average_gpa(cls):
if cls.count == 0: return 0
else: return cls.total_gpa / cls.count


student = Student("Spongbob", 3.2)
student = Student("Spongbob_2.0", 5)

print(f"Total number of student: {student.get_count()}")
# o/p: Total number of student: Total # of students: 2

print(f"Average GPA: {student.get_average_gpa()}")
# o/p: Average GPA: 4.1

Magic Methods

class Book:
def __init__(self, title, author, num_pages):
self.title = title
self.author = author
self.num_pages = num_pages

def __str__(self):
return f"{self.title} by {self.author}"

def __eq__(self, other):
return self.title == other.title and self.author == other.author

def __lt__(self, other):
return self.num_pages < other.num_pages

def __gt__(self, other):
return self.num_pages > other.num_pages

def __add__(self, other):
return f"{self.num_pages + other.num_pages} pages"

def __contains__(self, keyword):
return keyword in self.title or keyword in self.author

def __getitem__(self, key):
if key == "title": return self.title
elif key == "author": return self.author
elif key == "num_pages": return self.num_pages
else: return f"Key {key} not found"


book_1 = Book("The Hobbit", "J.R.R. Tolkien", 310)
book_2 = Book("Harry Potter", "J.K. Rowling", 223)
book_3 = Book("Harry Potter", "J.K. Rowling", 200)

print(book_1)
# o/p: The Hobbit by J.R.R. Tolkien

# __eq__
print(book_1 == book_2)
# o/p: False
print(book_2 == book_3)
# o/p: True

# __lt__
print(book_1 < book_2)
# o/p: False

# __gt__
print(book_1 > book_2)
# o/p: True

# __add__
print(book_1 + book_2)
# o/p: 533 pages

# __contains__
print("Rowling" in book_2)
# o/p: True

# __getitem__
print(book_1["title"])
# o/p: The Hobbit

print(book_2["author"])
# o/p: J.K. Rowling

print(book_3["num_pages"])
# o/p: 200

print(book_3["audio"])
# o/p: Key audio not found

Property

class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height

@property
def width(self):
return f"{self._width:.1f}cm"

@property
def height(self):
return f"{self._height:.1f}cm"

@width.setter
def width(self, new_width):
if new_width > 0:
self._width = new_width
else:
print("Width must be greater than zero")

@height.setter
def height(self, new_height):
if new_height > 0:
self.height = new_height
else:
print("Height must be greater than zero")

@width.deleter
def width(self):
del self._width
print("Width has been deleted")

@height.deleter
def height(self):
del self._height
print("Height has been deleted")


rectangle = Rectangle(3, 4)

# this is calling width property method
print(rectangle.width)
# o/p: 3.0cm

# this is calling height property method
print(rectangle.height)
# o/p: 4.0cm

rectangle.width = 0
# o/p: Width must be greater than zero

del rectangle.width
# o/p: Width has been deleted

print(rectangle.width)
# o/p: AttributeError: 'Rectangle' object has no attribute '_width'.
# Did you mean: 'width'?
info

To define private variables in python prefix it with _. For e.g. width is class variable, _width is private class variable. Accessing private variables in python gives warning not error.

Decorators

Decorators extends the functionality of the base function, without modifying the base function.

def add_sprinkles(func):
def wrapper():
print("You added sprinkles")
func()
return wrapper


# base function
@add_sprinkles
def get_ice_cream():
print("Here is your ice cream")


get_ice_cream()
# o/p:
# You added sprinkles
# Here is your ice cream
info

If you don't use inner wrapper in add_sprinkles function, add_sprinkles will be called even thought get_ice_cream function is not called.

Exception Handling

try:
number = int(input("Enter a number: "))
print(1 / number)
except ZeroDivisionError:
print("You can't divide by zero")
except ValueError:
print("Enter only number")
except Exception:
print("Something went wrong")
finally:
print("doing cleanup, before exiting")