Lesson 185 of 2116
Python Classes & OOP — Modeling Your World in Code
Classes let you bundle data with the behavior that operates on it. You'll build a class for a real thing and use AI to refactor it with confidence.
Lesson map
What this lesson covers
Learning path
The main moves in order
- 1When functions aren't enough
- 2class
- 3instance
- 4dataclass
Concept cluster
Terms to connect while reading
Section 1
When functions aren't enough
A function takes inputs and returns outputs. A class groups related functions with the data they share, so you stop passing the same five arguments to every call. In 2026 Python, @dataclass makes writing classes almost free.
@dataclass auto-generates __init__, __repr__, and __eq__. No boilerplate.
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class Task:
title: str
priority: int = 3
done: bool = False
created_at: datetime = field(default_factory=datetime.now)
def complete(self) -> None:
self.done = True
def age_seconds(self) -> float:
return (datetime.now() - self.created_at).total_seconds()
t = Task("Write essay", priority=1)
t.complete()
print(t) # Task(title='Write essay', priority=1, done=True, ...)self, methods, and instance data
- self is the first parameter of every instance method — Python passes the instance in automatically
- Methods are just functions that live inside a class
- Instance attributes are scoped to one object; class attributes are shared
Inheritance — build on what exists
RecurringTask gets everything Task has, plus new fields and methods.
@dataclass
class RecurringTask(Task):
interval_days: int = 7
def next_due(self) -> datetime:
from datetime import timedelta
return self.created_at + timedelta(days=self.interval_days)
r = RecurringTask("Water plants", interval_days=3)
print(r.next_due())
r.complete() # inherited from TaskError handling with typed exceptions
Custom exceptions let callers handle specific failures without parsing error strings.
class TaskError(Exception):
"""Base class for task errors."""
class TaskAlreadyDoneError(TaskError):
pass
@dataclass
class SafeTask(Task):
def complete(self) -> None:
if self.done:
raise TaskAlreadyDoneError(f"{self.title!r} is already done")
self.done = True
try:
t = SafeTask("Study")
t.complete()
t.complete() # second call raises
except TaskAlreadyDoneError as e:
print(f"Caught: {e}")Mini-exercise
- 1Design a BankAccount @dataclass with owner, balance, and transactions (list)
- 2Add methods deposit(amount) and withdraw(amount)
- 3withdraw should raise InsufficientFundsError if balance < amount
- 4Have your AI assistant write unit tests for the happy and failure paths
Compare the options
| Plain function | Class |
|---|---|
| Stateless | Holds state |
| Easy to test | Still easy with dataclass |
| Good for: one-shot logic | Good for: long-lived things (users, accounts, sessions) |
| No setup cost | A little boilerplate, a lot of clarity |
Big idea: classes model the nouns of your program. When you find yourself passing the same blob of data everywhere, it wants to be a class.
End-of-lesson quiz
Check what stuck
15 questions · Score saves to your progress.
Tutor
Curious about “Python Classes & OOP — Modeling Your World in Code”?
Ask anything about this lesson. I’ll answer using just what you’re reading — short, friendly, grounded.
Progress saved locally in this browser. Sign in to sync across devices.
Related lessons
Keep going
Creators · 45 min
Python Classes and OOP With AI
Classes group state and behavior. Dataclasses cut boilerplate. Let AI scaffold while you understand what's under the hood.
Creators · 50 min
The Landscape: Copilot vs. Cursor vs. Windsurf vs. Claude Code
The AI coding tool market fragmented fast. Let's map the 2026 landscape honestly: who is for autocomplete, who is for agents, who wins on cost, and what the tradeoffs actually feel like.
Creators · 50 min
Installing and Using Claude Code CLI
Claude Code is Anthropic's terminal-native coding agent. Let's install it, wire it to a project, and use the features most engineers miss on day one.
