PythonMastery

Use a dataclass for records, not a dict

@dataclass writes __init__, __repr__ and __eq__ for you. Typos become errors, fields have names, and frozen=True makes a record you can't change by accident.

A dict is the quickest way to hold a record, and the quickest way to misspell a key without anyone noticing. A @dataclass is one decorator and a list of fields, and in return Python writes the boring methods and catches the typos.

Before

python
order = {"id": "A-1001", "customer": "Ada", "total": 49.99}

order["totl"] = 59.99          # typo: a new key, not an update
print(order)
print(order == {"id": "A-1001", "customer": "Ada", "total": 49.99})
output
{'id': 'A-1001', 'customer': 'Ada', 'total': 49.99, 'totl': 59.99}
False

Nothing complained. The bug surfaces much later, somewhere else.

After

python
from dataclasses import dataclass, field

@dataclass
class Order:
    id: str
    customer: str
    total: float = 0.0
    items: list[str] = field(default_factory=list)

a = Order("A-1001", "Ada", 49.99)
b = Order("A-1001", "Ada", 49.99)

print(a)
print(a == b)
a.items.append("keyboard")
print(a.items, b.items)
output
Order(id='A-1001', customer='Ada', total=49.99, items=[])
True
['keyboard'] []

field(default_factory=list) gives each order its own list. A plain = [] would be shared by every order, the same trap as a default list in a function; dataclasses refuse it outright.

Freeze what shouldn't change

python
from dataclasses import dataclass, FrozenInstanceError

@dataclass(frozen=True)
class Reading:
    sensor: str
    celsius: float

r = Reading("roof", 21.5)
try:
    r.celsius = 99.0
except FrozenInstanceError as err:
    print("refused:", err)
output
refused: cannot assign to field 'celsius'

A frozen record can also be a dict key or a set member, because it can't change after it's hashed.

Why it works

The decorator reads the annotated fields and generates __init__, __repr__ and __eq__ from them. You get a readable print, comparison by value, and an editor that autocompletes order.customer instead of hoping you spell "customer" right.

When not to use it

For data that really is a mapping (JSON you pass straight through, counts keyed by word), keep the dict. If you need validation of incoming data, such as types checked at run time, a dataclass won't do that: its type hints are documentation, not checks.

Learn it properly: Dataclasses, Object-Oriented Programming