Week 6: Object-Oriented Programming
# Creating and managing cart state with functions - gets messy fast!
def create_cart():
return {"items": [], "subtotal": 0, "tax_rate": 0.08, "discount": 0}
def add_item(cart, name, price, quantity=1):
cart["items"].append({"name": name, "price": price, "qty": quantity})
cart["subtotal"] += price * quantity
return cart
def apply_discount(cart, discount_percent):
cart["discount"] = discount_percent
return cart
def calculate_total(cart):
discounted = cart["subtotal"] * (1 - cart["discount"]/100)
return discounted * (1 + cart["tax_rate"])
# Usage becomes cumbersome and error-prone
cart = create_cart()
cart = add_item(cart, "Laptop", 999.99)
cart = add_item(cart, "Mouse", 29.99)
cart = apply_discount(cart, 10)
total = calculate_total(cart)
print(f"Total: ${total:.2f}")
class ShoppingCart:
"""Represents a shopping cart with items, discounts, and tax calculation."""
def __init__(self, tax_rate=0.08):
self.items = []
self.tax_rate = tax_rate
self.discount_percent = 0
def add_item(self, name, price, quantity=1):
"""Add an item to the cart."""
self.items.append({"name": name, "price": price, "qty": quantity})
def apply_discount(self, discount_percent):
"""Apply a discount to the entire cart."""
self.discount_percent = discount_percent
@property
def subtotal(self):
"""Calculate subtotal before discount and tax."""
return sum(item["price"] * item["qty"] for item in self.items)
@property
def total(self):
"""Calculate final total after discount and tax."""
discounted = self.subtotal * (1 - self.discount_percent/100)
return discounted * (1 + self.tax_rate)
# Usage is clean, intuitive, and maintainable
cart = ShoppingCart()
cart.add_item("Laptop", 999.99)
cart.add_item("Mouse", 29.99)
cart.apply_discount(10)
print(f"Total: ${cart.total:.2f}")
Car
class is the blueprint; your specific Honda Civic is an object instance__init__
method: Object construction__init__
method is a special constructor functionself
parameter: References the specific instance being createdclass BankAccount:
"""Represents a bank account with balance tracking."""
def __init__(self, account_holder: str, initial_balance: float = 0.0):
# Instance attributes - unique to each account
self.account_holder = account_holder
self.balance = initial_balance
self.transaction_history = []
def deposit(self, amount: float):
"""Add money to the account."""
self.balance += amount
self.transaction_history.append(f"Deposited ${amount:.2f}")
# Create specific account instances
alice_account = BankAccount("Alice Johnson", 1000.0)
bob_account = BankAccount("Bob Smith") # Uses default balance of 0.0
__str__
method: Human-readable representation__str__
method defines how objects appear when printedprint()
, str()
, and string formattingclass User:
"""Represents a user in a social media application."""
def __init__(self, username: str, email: str, join_date: str):
self.username = username
self.email = email
self.join_date = join_date
self.followers = 0
self.is_verified = False
def __str__(self) -> str:
"""Return a user-friendly string representation."""
verification = "âś“" if self.is_verified else ""
return f"@{self.username}{verification} ({self.followers} followers) - Joined {self.join_date}"
# Create and display user instances
user1 = User("grace_hopper", "grace@example.com", "2023-01-15")
user2 = User("ada_lovelace", "ada@example.com", "2023-02-20")
user2.is_verified = True
user2.followers = 50000
print(user1) # @grace_hopper (0 followers) - Joined 2023-01-15
print(user2) # @ada_lovelaceâś“ (50000 followers) - Joined 2023-02-20
Book
class to model real-world books__init__
constructors and __str__
representationsself
as first parameterclass GameCharacter:
"""Represents a character in a role-playing game."""
def __init__(self, name: str, health: int = 100):
self.name = name
self.health = health
self.max_health = health
self.experience = 0
self.level = 1
def take_damage(self, damage: int):
"""Reduce character health, ensuring it doesn't go below 0."""
self.health = max(0, self.health - damage)
print(f"{self.name} takes {damage} damage! Health: {self.health}/{self.max_health}")
def heal(self, amount: int):
"""Restore character health, capped at maximum."""
old_health = self.health
self.health = min(self.max_health, self.health + amount)
healed = self.health - old_health
print(f"{self.name} heals for {healed} points! Health: {self.health}/{self.max_health}")
def gain_experience(self, exp: int):
"""Add experience and level up if threshold is reached."""
self.experience += exp
if self.experience >= self.level * 100: # Simple leveling formula
self.level += 1
self.max_health += 20
self.health = self.max_health # Full heal on level up
print(f"🎉 {self.name} reached level {self.level}!")
# Create and interact with a character
hero = GameCharacter("Aria the Brave")
hero.take_damage(30)
hero.heal(15)
hero.gain_experience(150)
super()
functionclass ChildClass(ParentClass):
establishes inheritance relationshipsuper()
function: Calls methods from the parent class__init__
must call parent __init__
using super()
class Vehicle:
"""Base class for all vehicles."""
def __init__(self, make: str, model: str, year: int):
self.make = make
self.model = model
self.year = year
self.mileage = 0
def start_engine(self):
"""Start the vehicle's engine."""
print(f"The {self.year} {self.make} {self.model} engine starts.")
def drive(self, miles: float):
"""Drive the vehicle and update mileage."""
self.mileage += miles
print(f"Drove {miles} miles. Total mileage: {self.mileage}")
class ElectricCar(Vehicle):
"""Electric vehicle with battery management."""
def __init__(self, make: str, model: str, year: int, battery_capacity: float):
super().__init__(make, model, year) # Call parent constructor
self.battery_capacity = battery_capacity
self.battery_level = 100.0 # Start fully charged
def start_engine(self):
"""Override: Electric cars don't have traditional engines."""
print(f"The {self.year} {self.make} {self.model} powers on silently.")
def charge(self, hours: float):
"""Charge the battery (unique to electric cars)."""
charge_added = min(hours * 10, 100 - self.battery_level)
self.battery_level += charge_added
print(f"Charged for {hours} hours. Battery: {self.battery_level:.1f}%")
# Inheritance in action
tesla = ElectricCar("Tesla", "Model S", 2023, 100.0)
tesla.start_engine() # Uses overridden method
tesla.drive(50) # Uses inherited method
tesla.charge(2) # Uses unique method
@property
decoratorclass Product:
"""Represents a product in an inventory system."""
# Class variable - shared across all instances
total_products_created = 0
def __init__(self, name: str, price: float):
self.name = name
self._price = price # Private attribute (convention)
Product.total_products_created += 1
@property
def price(self) -> float:
"""Get the product price."""
return self._price
@price.setter
def price(self, value: float):
"""Set the product price with validation."""
if value < 0:
raise ValueError("Price cannot be negative")
self._price = value
@classmethod
def get_total_products(cls) -> int:
"""Return total number of products created."""
return cls.total_products_created
@staticmethod
def calculate_tax(price: float, tax_rate: float = 0.08) -> float:
"""Calculate tax amount for a given price."""
return price * tax_rate
# Advanced features in action
laptop = Product("Gaming Laptop", 1299.99)
mouse = Product("Wireless Mouse", 79.99)
print(f"Total products: {Product.get_total_products()}") # Class method
print(f"Tax on laptop: ${Product.calculate_tax(laptop.price):.2f}") # Static method
# Property with validation
# laptop.price = -100 # Would raise ValueError
laptop.price = 1199.99 # Valid price change
Book
class with meaningful methodsEBook
class that inherits from Book
__str__
method in the child class__init__
for construction, __str__
for representationIS4010: App Development with AI