Skip to main content

Phase 6 🏛️ — OOP

Topics: Classes, objects, __init__, inheritance, methods, encapsulation

Object-Oriented Programming lets you model real-world entities as code. Classes are the blueprint; objects are the things you build from them.


🔄 Exercise Flow


📚 Prerequisites

Before starting these exercises, make sure you've read:


🔰 Starter: Bank Account Class

Time: 12 minutes

Design a BankAccount class with deposit, withdrawal, and balance display functionality.

Learning Objectives

  • Define a class with __init__ constructor
  • Create instance attributes (self.attr)
  • Define instance methods that modify state
  • Use encapsulation with private attributes (__attr)

Starter Code

# Starter: Bank Account Class

class BankAccount:
def __init__(self, owner, initial_balance=0):
# TODO: Set owner (public) and __balance (private)
pass

def deposit(self, amount):
# TODO: Add amount to balance if amount > 0
pass

def withdraw(self, amount):
# TODO: Subtract amount if balance is sufficient
pass

def get_balance(self):
# TODO: Return the current balance
pass

def __str__(self):
# TODO: Return a string like "Account owner: Alice | Balance: $500.00"
pass

# --- Test your class ---
account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account) # Account owner: Alice | Balance: $1300.00
print(account.get_balance()) # 1300
# print(account.__balance) # This should raise an AttributeError!

Expected Output

Account owner: Alice | Balance: $1300.00
1300

⭐ Medium: Library System with Inheritance

Time: 22 minutes

Build a library system with a base LibraryItem class and subclasses Book, DVD, and Magazine that inherit and extend its functionality.

Learning Objectives

  • Create a base class with shared attributes and methods
  • Use inheritance (class Child(Parent))
  • Override methods in subclasses
  • Use super() to call parent methods
  • Implement polymorphic behaviour

Starter Code

# Medium: Library System with Inheritance

class LibraryItem:
def __init__(self, title, item_id, is_borrowed=False):
self.title = title
self.item_id = item_id
self._is_borrowed = is_borrowed

def borrow(self):
if not self._is_borrowed:
self._is_borrowed = True
return f"'{self.title}' borrowed successfully."
return f"'{self.title}' is already borrowed."

def return_item(self):
if self._is_borrowed:
self._is_borrowed = False
return f"'{self.title}' returned successfully."
return f"'{self.title}' was not borrowed."

def __str__(self):
status = "Available" if not self._is_borrowed else "Borrowed"
return f"[{self.item_id}] {self.title}{status}"

# TODO 1: Create Book(LibraryItem) with author and page_count
# TODO 2: Create DVD(LibraryItem) with director and duration
# TODO 3: Create Magazine(LibraryItem) with issue_number
# TODO 4: Each subclass should override __str__ to include its extra info
# TODO 5: Book.borrow() should check if it's a reference copy (can't be borrowed)
# TODO 6: Create a Library class that manages a collection of LibraryItems

# Your code here 👇

Expected Output

=== LIBRARY CATALOG ===
[B001] Python Crash Course — Eric Matthes (350 pages) — Available
[B002] Reference: Python Guide (Reference Copy — Cannot be borrowed)
[D001] Inception — Christopher Nolan (148 min) — Available
[M001] Tech Monthly — Issue #42 — Borrowed

Operations:
✓ 'Python Crash Course' borrowed successfully.
✗ 'Reference: Python Guide' is a reference copy and cannot be borrowed.
✓ 'Python Crash Course' returned successfully.

🏆 Hard: E-Commerce System with Full OOP Design

Time: 40 minutes

Design a complete e-commerce system using OOP principles: inheritance, encapsulation, polymorphism, composition, and abstract base classes.

Learning Objectives

  • Use abstract base classes (ABC) and abstract methods
  • Implement multiple levels of inheritance
  • Use composition (a class containing instances of other classes)
  • Apply proper encapsulation with property decorators
  • Build a realistic, extensible system design

Starter Code

# Hard: E-Commerce System with Full OOP Design

from abc import ABC, abstractmethod

# --- Abstract Base Class ---
class Product(ABC):
def __init__(self, product_id, name, price, stock):
self._product_id = product_id
self._name = name
self._price = price
self._stock = stock

@property
def name(self):
return self._name

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

@property
def stock(self):
return self._stock

@stock.setter
def stock(self, value):
if value >= 0:
self._stock = value
else:
raise ValueError("Stock cannot be negative")

@abstractmethod
def get_description(self):
pass

@abstractmethod
def get_category(self):
pass

# TODO 1: Implement Electronics(Product) with warranty_months and brand
# TODO 2: Implement BookProduct(Product) with author and pages
# TODO 3: Implement Clothing(Product) with size and material
# TODO 4: Create a Customer class with name, email, loyalty_points
# TODO 5: Create an Order class that contains:
# - A customer
# - A list of OrderItem (product + quantity)
# - Methods to add_item, remove_item, get_total, apply_discount
# - Property to check if order qualifies for free shipping
# TODO 6: Create an Inventory class that manages all Products
# TODO 7: Implement a ShoppingCart (composition: cart contains products)

# Your code here 👇

Expected Output

═══════════════════════════════════════
PYTHONMART — OOP E-Commerce
═══════════════════════════════════════

Product Catalog:
[ELEC] Sony Wireless Headphones — $149.99 (2yr warranty) ⭐
[BOOK] Fluent Python — $49.99 by Luciano Ramalho (792 pages) 📖
[CLOTH] Python Logo Hoodie — $39.99 (Size: L, Cotton) 👕

Customer: Alice Johnson (Gold Member — 250 points)

🛒 Shopping Cart:
1. Sony Wireless Headphones x1 = $149.99
2. Fluent Python x2 = $99.98
3. Python Logo Hoodie x1 = $39.99
─────────────────────────────────
Subtotal: $289.96
Discount: -$28.99 (10% Gold Member)
Shipping: $0.00 (Free! Over $200)
─────────────────────────────────
Total: $260.97

📦 Order #ORD-2025-001 placed successfully!

💡 Tips for Success

  • self is always the first parameter — every instance method takes self as its first argument, referring to the current object.
  • Use @property for getters and setters — it's the Pythonic way to encapsulate attributes without writing get_balance() / set_balance() methods.
  • Favour composition over inheritance — "has-a" is often more flexible than "is-a". A Car has an Engine, rather than a Car being a subclass of Engine.
  • Abstract base classes define the interface — they force subclasses to implement specific methods, ensuring consistency.

← Back to Exercises