Crash Courses · PhD
Classes & Object-Oriented Programming
Model real-world systems using classes, inheritance, __dunder__ methods, and dataclasses.
16 min read+185 XP on completionCert: Python Crash Course
Tap any word in the text below to start reading from there.
Classes & Object-Oriented Programming
Defining a Class
class Product:
"""A product in an inventory system."""
# Class variable — shared across ALL instances
currency = "USD"
product_count = 0
def __init__(self, name, price, stock=0):
"""Initialize a Product instance."""
# Instance variables — unique to each object
self.name = name
self.price = price
self.stock = stock
Product.product_count += 1
def __str__(self):
"""Human-readable string — used by print()."""
return f"{self.name} (${self.price:.2f})"
def __repr__(self):
"""Unambiguous string — used in debugging/REPL."""
return f"Product(name={self.name!r}, price={self.price}, stock={self.stock})"
def apply_discount(self, percent):
"""Return a new price after applying discount."""
if not 0 < percent < 100:
raise ValueError(f"Discount must be between 0 and 100, got {percent}")
return self.price * (1 - percent / 100)
def restock(self, quantity):
if quantity <= 0:
raise ValueError("Restock quantity must be positive")
self.stock += quantity
return self.stock
laptop = Product("MacBook Pro", 2499.00, stock=5)
keyboard = Product("Mechanical Keyboard", 149.99)
print(laptop) # MacBook Pro ($2499.00)
print(repr(laptop)) # Product(name='MacBook Pro', price=2499.0, stock=5)
print(laptop.apply_discount(10)) # 2249.1
print(Product.product_count) # 2---
@property — Computed Attributes
class Circle:
def __init__(self, radius):
if radius <= 0:
raise ValueError("Radius must be positive")
self._radius = radius # _ prefix = "private by convention"
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Radius must be positive")
self._radius = value
@property
def area(self):
import math
return math.pi * self._radius ** 2
@property
def circumference(self):
import math
return 2 * math.pi * self._radius
c = Circle(5)
print(c.area) # 78.53981...
print(c.circumference) # 31.41592...
c.radius = 10 # uses the setter
c.radius = -1 # ValueError!---
Inheritance
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
def describe(self):
return f"{self.name} is a {self.species}"
def speak(self):
raise NotImplementedError("Subclasses must implement speak()")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name, species="Canis lupus familiaris")
self.breed = breed
def speak(self):
return f"{self.name} says: Woof!"
def fetch(self, item="ball"):
return f"{self.name} fetches the {item}!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow."
dog = Dog("Rex", "German Shepherd")
cat = Cat("Luna", "Felis catus")
print(dog.describe()) # Rex is a Canis lupus familiaris
print(dog.speak()) # Rex says: Woof!
print(dog.fetch("stick"))
print(cat.speak()) # Luna says: Meow.
# isinstance — checks class hierarchy
print(isinstance(dog, Animal)) # True
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Cat)) # False---
dataclasses — Less Boilerplate
from dataclasses import dataclass, field
from typing import List
@dataclass
class Transaction:
amount: float
description: str
transaction_type: str # "credit" or "debit"
def __post_init__(self):
if self.amount <= 0:
raise ValueError("Transaction amount must be positive")
if self.transaction_type not in ("credit", "debit"):
raise ValueError(f"Invalid type: {self.transaction_type}")
@dataclass
class Account:
owner: str
account_number: str
_balance: float = field(default=0.0, repr=False)
_transactions: List[Transaction] = field(default_factory=list, repr=False)
@property
def balance(self):
return self._balance
def deposit(self, amount, description="Deposit"):
t = Transaction(amount, description, "credit")
self._balance += amount
self._transactions.append(t)
return self._balance
def withdraw(self, amount, description="Withdrawal"):
if amount > self._balance:
raise ValueError(f"Insufficient funds (balance: {self._balance:.2f})")
t = Transaction(amount, description, "debit")
self._balance -= amount
self._transactions.append(t)
return self._balance
def history(self):
for t in self._transactions:
sign = "+" if t.transaction_type == "credit" else "-"
print(f" {sign}${t.amount:.2f} {t.description}")
acc = Account("Jordan", "ACC-001")
acc.deposit(1000, "Initial deposit")
acc.deposit(500, "Client payment")
acc.withdraw(200, "Rent")
print(f"Balance: ${acc.balance:.2f}")
acc.history()Reading progress
0% read
In this module
class
__init__
self
Inheritance
0%