Skip to content
Python Basics: Keeping Related Values in a Dataclass

Python Basics: Keeping Related Values in a Dataclass

October 12, 2026

A task has several related values: a name, a retry limit, and labels. A dataclass gives those values named fields without writing a constructor yourself.

Use Python 3.12 or newer on Linux. These independent examples work in memory.

Define a small record

Save as demo_dataclasses_1.py:

from dataclasses import dataclass

@dataclass
class Task:
    name: str
    retries: int = 2

task = Task("backup")
print(task)
print("Retries:", task.retries)
print("Same values:", task == Task("backup", 2))

Run it from the directory where you saved it:

python3 demo_dataclasses_1.py

Output:

Task(name='backup', retries=2)
Retries: 2
Same values: True

The decorator generates initialization, a readable representation, and equality based on fields. Put required fields before fields with defaults. Type annotations describe expected values; they do not validate them at runtime.

Give each record its own list

Save as demo_dataclasses_2.py:

from dataclasses import dataclass, field

@dataclass
class Task:
    name: str
    labels: list[str] = field(default_factory=list)

first = Task("backup")
second = Task("report")
first.labels.append("nightly")
print(first)
print(second)
print("Shared list:", first.labels is second.labels)

Run it from the directory where you saved it:

python3 demo_dataclasses_2.py

Output:

Task(name='backup', labels=['nightly'])
Task(name='report', labels=[])
Shared list: False

Pass list, not list(), to default_factory. Each instance gets a fresh list. A mutable field default such as labels: list[str] = [] is rejected.

Validate at construction

Save as demo_dataclasses_3.py:

from dataclasses import dataclass

@dataclass
class Task:
    name: str
    retries: int = 2

    def __post_init__(self):
        if type(self.retries) is not int or self.retries < 0:
            raise ValueError("retries must be a nonnegative integer")

for retries in (0, -1, "2", True):
    try:
        print(Task("backup", retries))
    except ValueError as error:
        print(error)

Run it from the directory where you saved it:

python3 demo_dataclasses_3.py

Output:

Task(name='backup', retries=0)
retries must be a nonnegative integer
retries must be a nonnegative integer
retries must be a nonnegative integer

The generated constructor calls __post_init__(). This check deliberately rejects booleans, which are integer subclasses. It validates only retries during construction; later assignments remain possible and need their own checks.

References

Acknowledgements

This post was created with assistance from AI.

Last updated on