hayaliv

hayaliv / Hi! Python / Level 2

Dataclasses

@dataclass writes __init__, __repr__ and __eq__ for you, so a record like Example(input, label) takes a few lines.

Hi! Python · Level 2: Level 2: Writing reliable code · Checked against the Python 3.12 documentation · Updated 2026-10-17

Think of it like this

A dataclass is like a printed form with labelled boxes. You only name the boxes (input, label). The print shop adds the border, the box numbers and the rule for comparing two filled forms.

Where the analogy stops being true. A paper form can reject a wrong entry at the desk, but a dataclass accepts any value you put in a box.

What to know

Example

from dataclasses import dataclass, field

@dataclass
class Example:
    input: str
    label: str = "unknown"
    tags: list[str] = field(default_factory=list)

a = Example("Call me Tuesday", "meeting_request")
b = Example("Call me Tuesday", "meeting_request")
print(a)
print(a == b)
print(Example("hello"))
print(Example("oops", 5))

It prints:

Example(input='Call me Tuesday', label='meeting_request', tags=[])
True
Example(input='hello', label='unknown', tags=[])
Example(input='oops', label=5, tags=[])

Predict, then check

1. What does this print?

from dataclasses import dataclass

@dataclass
class Pair:
    text: str
    label: str

print(Pair("hi", "x") == Pair("hi", "x"))
print(Pair("hi", "x") == Pair("hi", "y"))
Show the answer
True
False

The generated __eq__ compares the fields in order, so equal fields give True and one different field gives False.

2. What does this print?

from dataclasses import dataclass

@dataclass(frozen=True)
class Pair:
    text: str
    label: str

p = Pair("hi", "x")
try:
    p.label = "y"
except Exception as err:
    print(type(err).__name__)
print(p.label)
Show the answer
FrozenInstanceError
x

Assigning to a field of a frozen dataclass raises FrozenInstanceError. The except block prints its name and the label stays unchanged.

3. What does this print?

from dataclasses import dataclass, field

@dataclass
class Box:
    items: list = field(default_factory=list)

a = Box()
b = Box()
a.items.append(1)
print(a.items, b.items)
Show the answer
[1] []

default_factory builds a fresh list for every new Box, so changing a.items does not touch b.items.

4. What does this print? (The class is defined inside try.)

from dataclasses import dataclass

try:
    @dataclass
    class Bad:
        flags: list = []
except ValueError as err:
    print(err)
Show the answer
mutable default <class 'list'> for field flags is not allowed: use default_factory

Python refuses a plain list as a field default when @dataclass runs, and the ValueError message tells you to use default_factory.

Try it

Write a dataclass Reply with a text field (str), a label field (str, default "unknown") and a flags field (list of str, empty by default, a separate list for each instance).

class Reply:
    pass
Show a solution
from dataclasses import dataclass, field

@dataclass
class Reply:
    text: str
    label: str = "unknown"
    flags: list[str] = field(default_factory=list)

Common mistake

Do not write flags: list = [] inside a dataclass. Python refuses it with a ValueError, as the fourth predict item shows. Use field(default_factory=list) instead.

In a small-language-model project

In a fine-tuning project you usually load many labelled replies. An Example(input, label) dataclass gives each record a readable print and an == check for free. Because nothing checks types, validate labels yourself (see the Exceptions lesson).

Sources

Each quote below appears word for word on the cited page of the official Python documentation (version 3.12).

Step through this lesson in the Hi! Python desktop

← Regular expressions, the basicsType hints →