Foundations gave you dicts. Patterns added comprehensions. Why would you ever need anything else?
I don't know — what breaks?
Look at this shape. Three items, each with the same fields:
item_a = {"name": "a", "value": 5}
item_b = {"name": "b", "value": 12}
item_c = {"name": "c", "value": 3}Works fine. What if you want a function is_high(item) that returns item["value"] > 10? Also fine. Now imagine the project grows — twenty fields per item, fifty places that read them, three places that only set some of them. Every typo'd key (item["valeu"]) is a runtime KeyError. No editor hints. Nothing to grep.
And a class would help how?
A class is a named shape. The fields are declared once, in one place. Typos become attribute errors at the line they happen. Editor autocomplete works. Functions that operate on the shape can live with the shape, not scattered across the file.
So I should always use classes?
No — that's the L7 lesson. A class earns its place when (a) the same shape repeats, (b) you have non-trivial behaviour tied to it, or (c) you want type-checked fields. For one-off pairs, a tuple or dict is still right. Today: just see the pain that motivates classes.
Dicts are great for ad-hoc, one-off bags of keys. They start to hurt when:
item_a = {"name": "a", "value": 5}
item_b = {"name": "b", "value": 12}
item_c = {"name": "c", "value": 3}Three items, same fields. The shape (name, value) is implicit — written nowhere, just implied by the keys you happened to use. A new contributor writes item_d = {"name": "d", "val": 99} (typo) and the codebase quietly diverges.
item = {"name": "a", "value": 5}
print(item["valeu"]) # KeyError at runtime — no editor warningThe interpreter has no way to know valeu was meant to be value — keys are arbitrary strings.
Functions that operate on the shape end up spread across files:
def is_high(item):
return item["value"] > 10
def format(item):
return f"{item['name']}: {item['value']}"Nothing wrong with that — but it's hard to find all the operations on this shape. There's no central place to look.
@dataclass
class Item:
name: str
value: int
def is_high(self) -> bool:
return self.value > 10AttributeError at the line they happen — caught fasteris_high) lives with the shapeWeek 1 ends with an explicit "when NOT to use a class" lesson. Preview: if your data is a one-off pair, has no behaviour, and isn't repeated in a list of similar things, a dict or a tuple is the right tool. Classes are not the default — they're a tool for a specific shape of problem.
Given three dicts representing items, identify the field everyone has in common, and predict where the pain would show up if the codebase had 50 places reading these dicts. The exercise is just print(...) — no class yet. The point is to feel the shape that motivates the next five lessons.
Foundations gave you dicts. Patterns added comprehensions. Why would you ever need anything else?
I don't know — what breaks?
Look at this shape. Three items, each with the same fields:
item_a = {"name": "a", "value": 5}
item_b = {"name": "b", "value": 12}
item_c = {"name": "c", "value": 3}Works fine. What if you want a function is_high(item) that returns item["value"] > 10? Also fine. Now imagine the project grows — twenty fields per item, fifty places that read them, three places that only set some of them. Every typo'd key (item["valeu"]) is a runtime KeyError. No editor hints. Nothing to grep.
And a class would help how?
A class is a named shape. The fields are declared once, in one place. Typos become attribute errors at the line they happen. Editor autocomplete works. Functions that operate on the shape can live with the shape, not scattered across the file.
So I should always use classes?
No — that's the L7 lesson. A class earns its place when (a) the same shape repeats, (b) you have non-trivial behaviour tied to it, or (c) you want type-checked fields. For one-off pairs, a tuple or dict is still right. Today: just see the pain that motivates classes.
Dicts are great for ad-hoc, one-off bags of keys. They start to hurt when:
item_a = {"name": "a", "value": 5}
item_b = {"name": "b", "value": 12}
item_c = {"name": "c", "value": 3}Three items, same fields. The shape (name, value) is implicit — written nowhere, just implied by the keys you happened to use. A new contributor writes item_d = {"name": "d", "val": 99} (typo) and the codebase quietly diverges.
item = {"name": "a", "value": 5}
print(item["valeu"]) # KeyError at runtime — no editor warningThe interpreter has no way to know valeu was meant to be value — keys are arbitrary strings.
Functions that operate on the shape end up spread across files:
def is_high(item):
return item["value"] > 10
def format(item):
return f"{item['name']}: {item['value']}"Nothing wrong with that — but it's hard to find all the operations on this shape. There's no central place to look.
@dataclass
class Item:
name: str
value: int
def is_high(self) -> bool:
return self.value > 10AttributeError at the line they happen — caught fasteris_high) lives with the shapeWeek 1 ends with an explicit "when NOT to use a class" lesson. Preview: if your data is a one-off pair, has no behaviour, and isn't repeated in a list of similar things, a dict or a tuple is the right tool. Classes are not the default — they're a tool for a specific shape of problem.
Given three dicts representing items, identify the field everyone has in common, and predict where the pain would show up if the codebase had 50 places reading these dicts. The exercise is just print(...) — no class yet. The point is to feel the shape that motivates the next five lessons.
Create a free account to get started. Paid plans unlock all tracks.