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
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'?
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
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")