Object-Oriented Basics
__init__
The Constructor
Creating a User without a name makes no sense. The __init__ method is Python's
constructor - it runs automatically when you create an object, letting you
require and validate data from the start.
Basic constructor
Initialize objects with required data.
# Basic __init__ Constructor
print("=== Basic Constructor ===\n")
class Dog:
def __init__(self, name, breed):
print(f"Creating dog: {name}, {breed}")
self.name = name
self.breed = breed
# Create dogs - __init__ called automatically
buddy = Dog("Buddy", "Golden Retriever")
max_dog = Dog("Max", "German Shepherd")
print(f"\nbuddyname: {buddy.name}, breed: {buddy.breed}")
print(f"max name: {max_dog.name}, breed: {max_dog.breed}")
print("\n=== Without Constructor ===")
class EmptyClass:
pass
# Can still create but no initialization
obj = EmptyClass()
print(f"Empty object: {obj}")
# Must add attributes manually
obj.value = 42
print(f"After manual: {obj.value}")
print("\n=== Constructor vs Manual ===")
# With constructor - guaranteed state
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4) # Always has x and y
print(f"Point: ({p.x}, {p.y})")
# Without - inconsistent state
class BadPoint:
pass
bp = BadPoint()
bp.x = 3
# Forgot to set y!
# print(bp.y) # Would raise AttributeError
print("\n=== Multiple Parameters ===")
class Rectangle:
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
def describe(self):
return f"Rectangle at ({self.x},{self.y}) size {self.width}x{self.height}"
rect = Rectangle(10, 20, 100, 50)
print(rect.describe())
__init__ runs when object is created. Sets up initial state.
Default parameter values
Make some parameters optional.
# Default Values in Constructors
print("=== Default Parameter Values ===\n")
class User:
def __init__(self, username, role="user", active=True):
self.username = username
self.role = role
self.active = active
def describe(self):
status = "active" if self.active else "inactive"
return f"{self.username} ({self.role}, {status})"
# Different ways to create
admin = User("admin", "administrator", True) # All explicit
alice = User("alice", "editor") # Default active
bob = User("bob") # All defaults
print(admin.describe())
print(alice.describe())
print(bob.describe())
print("\n=== Named Arguments ===")
# Use named args to skip defaults
guest = User("guest", active=False) # Skip role
print(guest.describe())
# Mix positional and named
mod = User("mod", role="moderator")
print(mod.describe())
print("\n=== Mutable Default Values ===")
# WRONG - Shared mutable default!
class BadList:
def __init__(self, items=[]):
self.items = items
bad1 = BadList()
bad2 = BadList()
bad1.items.append("item1")
print(f"bad1.items: {bad1.items}")
print(f"bad2.items: {bad2.items}") # Also has item1!
print("\n=== Correct Pattern for Mutables ===")
# CORRECT - Use None and create new list
class GoodList:
def __init__(self, items=None):
self.items = items if items else []
good1 = GoodList()
good2 = GoodList()
good1.items.append("item1")
print(f"good1.items: {good1.items}")
print(f"good2.items: {good2.items}") # Empty - correct!
print("\n=== Default with Various Types ===")
class GameCharacter:
def __init__(self, name, health=100, inventory=None,
position=None, multiplier=1.0):
self.name = name
self.health = health
self.inventory = inventory if inventory else []
self.position = position if position else {"x": 0, "y": 0}
self.multiplier = multiplier
# Create with defaults
hero = GameCharacter("Hero")
print(f"{hero.name}: health={hero.health}, pos={hero.position}")
print(f"Inventory: {hero.inventory}")
# Create with custom values
wizard = GameCharacter("Wizard", 80, ["staff", "spellbook"],
{"x": 10, "y": 5}, 1.5)
print(f"\n{wizard.name}: health={wizard.health}, pos={wizard.position}")
print(f"Inventory: {wizard.inventory}")
Parameters with defaults can be omitted: def __init__(self, name, age=0).
Validate input
Reject invalid data at creation time.
# Input Validation in Constructors
print("=== Validation with Errors ===\n")
class Age:
def __init__(self, years):
if not isinstance(years, int):
raise TypeError("Age must be an integer")
if years < 0:
raise ValueError("Age cannot be negative")
if years > 150:
raise ValueError("Age cannot exceed 150")
self.years = years
# Valid ages
age1 = Age(25)
age2 = Age(0)
print(f"Valid ages: {age1.years}, {age2.years}")
# Invalid ages (uncomment to see errors)
# Age("twenty") # TypeError
# Age(-5) # ValueError
# Age(200) # ValueError
print("\n=== Validation with Correction ===")
class Username:
def __init__(self, name):
# Strip whitespace
name = name.strip()
# Convert to lowercase
name = name.lower()
# Validate length
if len(name) < 3:
raise ValueError("Username must be at least 3 characters")
if len(name) > 20:
name = name[:20] # Truncate
self.name = name
user1 = Username(" Alice ") # Whitespace removed
user2 = Username("BOB") # Lowercased
user3 = Username("VeryLongUsernameThatExceedsLimit") # Truncated
print(f"'{user1.name}'")
print(f"'{user2.name}'")
print(f"'{user3.name}' (len={len(user3.name)})")
print("\n=== Validation with Defaults ===")
class Temperature:
def __init__(self, celsius):
# Clamp to valid range
if celsius < -273.15: # Absolute zero
print(f"Warning: {celsius} adjusted to -273.15")
celsius = -273.15
self.celsius = celsius
@property
def fahrenheit(self):
return self.celsius * 9/5 + 32
t1 = Temperature(25)
t2 = Temperature(-300) # Adjusted
print(f"t1: {t1.celsius}°C")
print(f"t2: {t2.celsius}°C (was adjusted)")
print("\n=== Complex Validation ===")
class Email:
def __init__(self, address):
# Basic email validation
address = address.strip().lower()
if "@" not in address:
raise ValueError("Email must contain @")
parts = address.split("@")
if len(parts) != 2:
raise ValueError("Email must have exactly one @")
local, domain = parts
if not local:
raise ValueError("Email must have local part")
if not domain or "." not in domain:
raise ValueError("Email must have valid domain")
self.address = address
self.local = local
self.domain = domain
email = Email(" User@Example.COM ")
print(f"Address: {email.address}")
print(f"Local: {email.local}, Domain: {email.domain}")
print("\n=== Validation Summary ===")
class Product:
def __init__(self, name, price, quantity=0):
# Name validation
if not name or not name.strip():
raise ValueError("Product name cannot be empty")
self.name = name.strip()
# Price validation
if not isinstance(price, (int, float)):
raise TypeError("Price must be a number")
if price < 0:
raise ValueError("Price cannot be negative")
self.price = round(price, 2) # Round to cents
# Quantity validation
if not isinstance(quantity, int):
raise TypeError("Quantity must be an integer")
if quantity < 0:
quantity = 0 # Auto-correct
self.quantity = quantity
laptop = Product(" Laptop ", 999.999, 5)
print(f"Product: {laptop.name}, ${laptop.price}, qty={laptop.quantity}")
Raise exceptions in __init__ to prevent invalid objects from existing.
Computed attributes
Calculate attributes from parameters.
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
# Computing Attributes in Constructor
print("=== Computed from Parameters ===\n")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
# Computed attributes
self.area = width * height
self.perimeter = 2 * (width + height)
self.diagonal = (width**2 + height**2) ** 0.5
rect =
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
print(f"Diagonal: {rect.diagonal}")
print("\n=== Derived Attributes ===")
class Person:
def __init__(self, first_name, last_name, birth_year):
self.first_name = first_name
self.last_name = last_name
self.birth_year = birth_year
# Derived attributes
self.full_name = f"{first_name} {last_name}"
self.age = 2024 - birth_year
self.initials = f"{first_name[0]}.{last_name[0]}."
person = Person("John", "Doe", 1990)
print(f"Name: {person.full_name}")
print(f"Initials: {person.initials}")
print(f"Age: {person.age}")
print("\n=== Computed Collections ===")
class ScoreBoard:
def __init__(self, scores):
self.scores = list(scores) # Copy the list
# Compute statistics
self.count = len(self.scores)
self.total = sum(self.scores)
self.average = self.total / self.count if self.count else 0
self.highest = max(self.scores) if self.scores else None
self.lowest = min(self.scores) if self.scores else None
board =
print(f"Scores: {board.scores}")
print(f"Count: {board.count}, Total: {board.total}")
print(f"Average: {board.average:.1f}")
print(f"High: {board.highest}, Low: {board.lowest}")
print("\n=== Computed Flags/Status ===")
class Order:
def __init__(self, items, discount_code=None):
self.items = items
self.discount_code = discount_code
# Compute totals
self.subtotal = sum(item["price"] * item["qty"] for item in items)
# Apply discount
self.discount = 0
if discount_code == "SAVE10":
self.discount = self.subtotal * 0.10
elif discount_code == "SAVE20":
self.discount = self.subtotal * 0.20
self.total = self.subtotal - self.discount
# Status flags
self.is_large_order = self.total > 100
self.has_discount = self.discount > 0
self.item_count = sum(item["qty"] for item in items)
items = [
{"name": "Book", "price": 15, "qty": 2},
{"name": "Pen", "price": 5, "qty": 5}
]
order = Order(items, "SAVE10")
print(f"Items: {order.item_count}")
print(f"Subtotal: ${order.subtotal}")
print(f"Discount: ${order.discount}")
print(f"Total: ${order.total}")
print(f"Large order? {order.is_large_order}")
print("\n=== Timestamp and ID ===")
import time
class Event:
_next_id = 1 # Class variable for ID generation
def __init__(self, name, category):
self.name = name
self.category = category
# Auto-generated
self.id = Event._next_id
Event._next_id += 1
self.created_at = time.time()
e1 = Event("Login", "auth")
e2 = Event("Click", "ui")
e3 = Event("Purchase", "sale")
for e in [e1, e2, e3]:
print(f"Event #{e.id}: {e.name} ({e.category})")
Store computed values: self.area = width * height.
Common initialization patterns
Flexible constructors with various parameter styles.
# Common Initialization Patterns
print("=== Factory-Style Constructor ===\n")
class Color:
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
@classmethod
def from_hex(cls, hex_code):
"""Create Color from hex string like '#FF0000'"""
hex_code = hex_code.lstrip('#')
r = int(hex_code[0:2], 16)
g = int(hex_code[2:4], 16)
b = int(hex_code[4:6], 16)
return cls(r, g, b)
@classmethod
def from_name(cls, name):
colors = {
"red": (255, 0, 0),
"green": (0, 255, 0),
"blue": (0, 0, 255),
"white": (255, 255, 255),
"black": (0, 0, 0)
}
rgb = colors.get(name.lower(), (0, 0, 0))
return cls(*rgb)
def __str__(self):
return f"RGB({self.r}, {self.g}, {self.b})"
# Multiple ways to create
c1 = Color(255, 128, 0) # Direct
c2 = Color.from_hex("#00FF00") # From hex
c3 = Color.from_name("blue") # From name
print(f"Direct: {c1}")
print(f"From hex: {c2}")
print(f"From name: {c3}")
print("\n=== Configuration Object ===")
class Config:
def __init__(self, **kwargs):
# Set defaults
self.debug = False
self.log_level = "INFO"
self.max_connections = 100
self.timeout = 30
# Override with provided values
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
else:
print(f"Warning: Unknown config '{key}'")
def display(self):
print(f" debug: {self.debug}")
print(f" log_level: {self.log_level}")
print(f" max_connections: {self.max_connections}")
print(f" timeout: {self.timeout}")
# Default config
cfg1 = Config()
print("Default config:")
cfg1.display()
# Custom config
cfg2 = Config(debug=True, timeout=60, unknown="ignored")
print("\nCustom config:")
cfg2.display()
print("\n=== Copy Constructor Pattern ===")
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def copy(self):
"""Create a copy of this point"""
return Point(self.x, self.y)
@classmethod
def from_point(cls, other):
"""Create from another Point"""
return cls(other.x, other.y)
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(10, 20)
p2 = p1.copy() # Using instance method
p3 = Point.from_point(p1) # Using classmethod
p2.x = 100 # Change copy
print(f"Original: {p1}")
print(f"Copy (changed): {p2}")
print(f"From point: {p3}")
print("\n=== Dependent Initialization ===")
class Database:
def __init__(self, host, port, database):
self.host = host
self.port = port
self.database = database
# Build connection string
self.connection_string = f"db://{host}:{port}/{database}"
# Initialize state
self.connected = False
self.queries_run = 0
def connect(self):
print(f"Connecting to {self.connection_string}...")
self.connected = True
def query(self, sql):
if not self.connected:
raise RuntimeError("Not connected")
self.queries_run += 1
print(f"Running query #{self.queries_run}: {sql}")
db = Database("localhost", 5432, "myapp")
print(f"Connection string: {db.connection_string}")
db.connect()
db.query("SELECT * FROM users")
Use *args, **kwargs, or factory methods for flexible initialization.
Exercise: practical.py
Build a class with validation and computed attributes