🧪 Practice: OOP
Six exercises that turn class theory into class instinct. Solutions at the bottom.
Exercise 1 — The BankAccount
Create a BankAccount class with owner and balance (default 0). Methods: deposit(amount) (reject negatives by raising ValueError), withdraw(amount) (reject if insufficient funds), __str__ showing "Owner: ₹balance".
acc = BankAccount("Aarav")
acc.deposit(500)
acc.withdraw(200)
print(acc) # Aarav: ₹300
acc.withdraw(1000) # ValueError!Exercise 2 — The Rectangle
A Rectangle class with width and height. Add methods area(), perimeter(), and is_square() (returns a bool).
r = Rectangle(4, 4)
print(r.area()) # 16
print(r.is_square()) # TrueExercise 3 — The Animal family (inheritance)
Base class Animal with __init__(name) and speak() printing "...". Create Dog and Cat children that override speak(). Create a list of all three and loop it, calling speak on each.
# Expected:
# ...
# Rocky says Woof!
# Whiskers says Meow!Exercise 4 — Dataclass book
Rewrite this as a @dataclass with generated init/eq/repr, plus one method is_long() (True if pages > 300):
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pagesExercise 5 — The library (objects in containers)
Using your Book dataclass, create a Library class holding a list of books, with add_book(book) and find_by_author(author) returning matching books.
Exercise 6 — Fix the broken class
class Counter:
def __init__(count):
count = 0
def increment():
count += 1
c = Counter()
c.increment() # crashes!
print(c.count) # and this tooSolutions
Exercise 1:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
self.balance += amount
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
def __str__(self):
return f"{self.owner}: ₹{self.balance}"
acc = BankAccount("Aarav")
acc.deposit(500)
acc.withdraw(200)
print(acc)Exercise 2:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def is_square(self):
return self.width == self.heightExercise 3:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("...")
class Dog(Animal):
def speak(self):
print(f"{self.name} says Woof!")
class Cat(Animal):
def speak(self):
print(f"{self.name} says Meow!")
zoo = [Animal("Mystery"), Dog("Rocky"), Cat("Whiskers")]
for animal in zoo:
animal.speak()(Notice the loop doesn't care which type each animal is — polymorphism: same call, right behavior.)
Exercise 4:
from dataclasses import dataclass
@dataclass
class Book:
title: str
author: str
pages: int
def is_long(self):
return self.pages > 300Exercise 5:
@dataclass
class Library:
books: list = field(default_factory=list)
def add_book(self, book):
self.books.append(book)
def find_by_author(self, author):
return [b for b in self.books if b.author == author]
lib = Library()
lib.add_book(Book("Python Basics", "Galvan", 250))
lib.add_book(Book("Advanced Python", "Galvan", 400))
print(lib.find_by_author("Galvan"))(That list comprehension inside find_by_author is a preview of Module 10.)
Exercise 6:
class Counter:
def __init__(self): # self was missing
self.count = 0 # count = 0 created a LOCAL, not an attribute
def increment(self): # self was missing here too
self.count += 1 # and must go through self
c = Counter()
c.increment()
print(c.count) # 1Both bugs were self bugs — the exact mistakes Module 9 predicted.
✅ Module 9 Checkpoint
Classes built, inherited, dataclassed, and debugged. You can now read the class-based code inside every framework and library you'll ever install.
Next module: Power Tools — comprehensions, generators, and the standard library.