Python OOP Exercise Questions

Last Updated : 6 Oct, 2026

Python Object-Oriented Programming exercises help in understanding classes, objects, inheritance, encapsulation, polymorphism and other features through practical problems. This collection starts with basic class-based programs and gradually moves to coding problems commonly used in interviews.

  • Each exercise focuses on a specific OOP concept.
  • The questions include practical and edge-case based problems.
  • Later exercises combine multiple OOP concepts in a single program.

Example Programs

Exercise 1: Create a class Student with attributes name and marks. Create an object and display the student's details.

Python
class Student:
    def __init__(self, name, marks):
        self.name = name
        self.marks = marks

    def display(self):
        print("Name:", self.name)
        print("Marks:", self.marks)


student = Student("Rahul", 85)
student.display()

Output
Name: Rahul
Marks: 85

Explanation:

  • Student class stores the student's name and marks.
  • display() method prints the values stored in the object.
  • Object student represents one student created from the class.

Exercise 2: Create a class Employee with a class variable company and an instance variable name. Display the details of two employees.

Python
class Employee:
    company = "TechCorp"

    def __init__(self, name):
        self.name = name

    def display(self):
        print(self.name, "-", self.company)


emp1 = Employee("Amit")
emp2 = Employee("Neha")

emp1.display()
emp2.display()

Output
Amit - TechCorp
Neha - TechCorp

Explanation:

  • Company is shared by all Employee objects.
  • Name is stored separately for each object.
  • Both objects access the same class variable.

Exercise 3: Create a BankAccount class with methods to deposit money, withdraw money and display the current balance. Preventing withdrawal when the balance is insufficient.

Python
class BankAccount:
    def __init__(self, balance=0):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount
        else:
            print("Insufficient balance")

    def display_balance(self):
        print("Balance:", self.balance)


account = BankAccount(1000)

account.deposit(500)
account.withdraw(300)
account.display_balance()

Output
Balance: 1200

Explanation:

  • Balance is updated whenever money is deposited or withdrawn.
  • Withdrawal is allowed only when sufficient balance is available.
  • Constructor provides an initial balance.

Exercise 4: Create a class UserAccount with a private password attribute. Providing a method to change the password only when the old password is correct.

Python
class UserAccount:
    def __init__(self, password):
        self.__password = password

    def change_password(self, old_password, new_password):
        if self.__password == old_password:
            self.__password = new_password
            print("Password changed successfully")
        else:
            print("Incorrect password")


user = UserAccount("abc123")

user.change_password("abc123", "xyz789")
user.change_password("abc123", "new456")

Output
Password changed successfully
Incorrect password

Explanation:

  • Password is stored using a private attribute.
  • Password can be changed only through the change_password() method.
  • Second attempt fails because the old password is no longer valid.

Exercise 5: Create a Product class with a price property. The price should not accept negative values.

Python
class Product:
    def __init__(self, price):
        self.price = price

    @property
    def price(self):
        return self.__price

    @price.setter
    def price(self, value):
        if value < 0:
            print("Price cannot be negative")
        else:
            self.__price = value


product = Product(500)
print("Price:", product.price)

product.price = -100
print("Price:", product.price)

Output
Price: 500
Price cannot be negative
Price: 500

Explanation:

  • Property controls how the price is accessed and modified.
  • Negative values are rejected by the setter.
  • Existing valid price remains unchanged after an invalid assignment.

Exercise 6: Create a Vehicle class with a method start(). Create a Car class that inherits from Vehicle and adds a drive() method.

Python
class Vehicle:
    def start(self):
        print("Vehicle started")


class Car(Vehicle):
    def drive(self):
        print("Car is driving")


car = Car()

car.start()
car.drive()

Explanation:

  • Car inherits the start() method from Vehicle.
  • Car also defines its own drive() method.
  • Object can use methods from both classes.

Exercise 7: Create a parent class Animal with a sound() method. Overriding this method in Dog and Cat classes.

Python
class Animal:
    def sound(self):
        print("Animal makes a sound")


class Dog(Animal):
    def sound(self):
        print("Dog barks")


class Cat(Animal):
    def sound(self):
        print("Cat meows")


dog = Dog()
cat = Cat()

dog.sound()
cat.sound()

Output
Dog barks
Cat meows

Explanation:

  • Both child classes provide their own implementation of sound().
  • Method called depends on the object's class.
  • This demonstrates runtime polymorphism.

Exercise 8: Create different payment classes such as CreditCard and UPI. Each class should implement a pay() method. Writing a function that works with either object.

Python
class CreditCard:
    def pay(self, amount):
        print("Paid", amount, "using Credit Card")


class UPI:
    def pay(self, amount):
        print("Paid", amount, "using UPI")


def make_payment(payment_method, amount):
    payment_method.pay(amount)


make_payment(CreditCard(), 500)
make_payment(UPI(), 300)

Output
Paid 500 using Credit Card
Paid 300 using UPI

Explanation:

  • Function does not need to know the exact class of the object.
  • It only expects the object to provide a pay() method.
  • This demonstrates polymorphism through common behavior.

Exercise 9: Create a Point class and overload the + operator so that two Point objects can be added together.

Python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    def display(self):
        print("(", self.x, ",", self.y, ")")


p1 = Point(2, 3)
p2 = Point(4, 5)

p3 = p1 + p2
p3.display()

Output
( 6 , 8 )

Explanation:

  • add() method defines how the + operator works for Point objects.
  • x and y coordinates of both objects are added separately.
  • A new Point object is returned.

Exercise 10: Create a Book class and implement str() so that printing the object displays meaningful information instead of the default object representation.

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

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


book = Book("Python Programming", "John")
print(book)

Output
Python Programming by John

Explanation:

  • str() controls the string representation shown when the object is printed.
  • Without str(), Python would display the default object representation.

Exercise 11: Create a class Employee that keeps track of how many Employee objects have been created.

Python
class Employee:
    count = 0

    def __init__(self, name):
        self.name = name
        Employee.count += 1


emp1 = Employee("A")
emp2 = Employee("B")
emp3 = Employee("C")

print("Total employees:", Employee.count)

Explanation:

  • Count is a class variable shared by all Employee objects.
  • Constructor increments count whenever a new object is created.
  • Final value represents the number of objects created.

Exercise 12: Create a Student class that stores marks and returns the highest mark. Handle the case where no marks are provided.

Python
class Student:
    def __init__(self, marks):
        self.marks = marks

    def highest_mark(self):
        if not self.marks:
            return "No marks available"
        return max(self.marks)


student1 = Student([78, 92, 85])
student2 = Student([])

print(student1.highest_mark())
print(student2.highest_mark())

Output
92
No marks available

Explanation:

  • The method checks whether the marks list is empty before calling max().
  • This prevents an exception when no marks are available.

Exercise 13: Create an Engine class and a Car class. The Car should create and use an Engine object.

Python
class Engine:
    def start(self):
        print("Engine started")


class Car:
    def __init__(self):
        self.engine = Engine()

    def start(self):
        self.engine.start()
        print("Car started")


car = Car()
car.start()

Output
Engine started
Car started

Explanation:

  • Car contains an Engine object as one of its attributes.
  • Starting the car uses the Engine object's start() method.
  • This demonstrates composition between classes.

Exercise 14: Create an abstract Shape class with an area() method. Create Circle and Rectangle classes that provide their own implementations.

Python
from abc import ABC, abstractmethod


class Shape(ABC):
    @abstractmethod
    def area(self):
        pass


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

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


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

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


circle = Circle(5)
rectangle = Rectangle(4, 6)

print("Circle area:", circle.area())
print("Rectangle area:", rectangle.area())

Output
Circle area: 78.5
Rectangle area: 24

Explanation:

  • Shape defines area() as an abstract method.
  • Circle and Rectangle provide their own implementations.
  • Common interface allows different shape objects to calculate their area differently.

Exercise 15: Create an Employee class with a basic salary. Create a Manager class that inherits from Employee and adds a bonus. Calculate the final salary.

Python
class Employee:
    def __init__(self, salary):
        self.salary = salary

    def total_salary(self):
        return self.salary


class Manager(Employee):
    def __init__(self, salary, bonus):
        super().__init__(salary)
        self.bonus = bonus

    def total_salary(self):
        return self.salary + self.bonus


employee = Employee(30000)
manager = Manager(50000, 10000)

print("Employee Salary:", employee.total_salary())
print("Manager Salary:", manager.total_salary())

Output
Employee Salary: 30000
Manager Salary: 60000

Explanation:

  • Employee provides the basic salary calculation.
  • Manager overrides total_salary() to include the bonus.
  • super() is used to initialize the inherited salary attribute.

Exercise 16: Create a Product class and a ShoppingCart class. The cart should allow products to be added and calculate the total price.

Python
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price


class ShoppingCart:
    def __init__(self):
        self.products = []

    def add_product(self, product):
        self.products.append(product)

    def total_price(self):
        return sum(product.price for product in self.products)


cart = ShoppingCart()

cart.add_product(Product("Laptop", 50000))
cart.add_product(Product("Mouse", 1000))
cart.add_product(Product("Keyboard", 2000))

print("Total:", cart.total_price())

Output
Total: 53000

Explanation:

  • Product stores the details of individual products.
  • ShoppingCart maintains a collection of Product objects.
  • total_price() method calculates the combined price of all products.
  • Program demonstrates object composition and interaction between classes.

Exercise 17: Create a diamond inheritance structure and use super() to observe the order in which constructors are called.

Python
class A:
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        super().__init__()
        print("B")


class C(A):
    def __init__(self):
        super().__init__()
        print("C")


class D(B, C):
    def __init__(self):
        super().__init__()
        print("D")


D()

Output
A
C
B
D

Explanation:

  • D inherits from both B and C.
  • super() follows the Method Resolution Order rather than simply calling the direct parent.
  • Order demonstrates how cooperative multiple inheritance works in Python.

Exercise 18: Create a Library class that stores books and allows users to borrow and return them. A book should not be borrowed if it is already issued.

Python
class Book:
    def __init__(self, title):
        self.title = title
        self.available = True


class Library:
    def __init__(self):
        self.books = []

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

    def borrow_book(self, title):
        for book in self.books:
            if book.title == title:
                if book.available:
                    book.available = False
                    print(title, "borrowed")
                else:
                    print(title, "is already issued")
                return
        print("Book not found")

    def return_book(self, title):
        for book in self.books:
            if book.title == title:
                book.available = True
                print(title, "returned")
                return
        print("Book not found")


library = Library()

library.add_book(Book("Python"))
library.add_book(Book("Data Structures"))

library.borrow_book("Python")
library.borrow_book("Python")
library.return_book("Python")

Output
Python borrowed
Python is already issued
Python returned

Explanation:

  • Book stores the state of an individual book.
  • Library manages multiple Book objects.
  • Available attribute prevents the same book from being borrowed twice.
  • Program combines classes, objects, composition and state management.
Comment