Skip to content

Dataclasses and namedtuples

Beginning of the lesson

Dataclasses and namedtuples

Structured records without the boilerplate

Big Idea: A dataclass is a normal class whose repetitive plumbing — __init__, __repr__, __eq__ and more — is generated for you from type-annotated attributes. A namedtuple is an immutable tuple whose positions also have names. Both let you model a small bundle of related fields clearly; the rest of this topic is about picking the right one and driving it with field() and frozen=True.
By the end you will be able to:
  • replace hand-written __init__/__repr__/__eq__ with @dataclass,
  • give fields defaults safely, including mutable defaults via field(default_factory=...),
  • make records immutable and hashable with frozen=True,
  • tune individual fields with field() (repr, compare, init, kw_only),
  • choose between a dataclass, a NamedTuple, a plain class and a dict for a given job.

1. The boilerplate problem

You already know how to write a small record class by hand. The trouble is that even a three-field record forces you to repeat every name three or four times just to get sensible construction, printing and equality.

class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return (self.x, self.y) == (other.x, other.y)

That is a lot of ceremony for "an x and a y". Miss the __eq__ and two equal points compare unequal; miss the __repr__ and your logs show <Point object at 0x...>. Dataclasses generate exactly this plumbing from the annotations.

2. @dataclass in one line

Decorate a class with @dataclass and list its fields as class-level annotations. The decorator reads those annotations and writes __init__, __repr__ and __eq__ for you at class-creation time.

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(p)            # Point(x=1.0, y=2.0)
print(p == Point(1.0, 2.0))   # True

The annotation is what matters: x: float declares a field. A bare x = 0 without an annotation is not a field — it is an ordinary class attribute the dataclass machinery ignores. The type is not enforced at runtime; it is documentation that tools and readers use, and the marker that turns a name into a field.

By default you also get keyword or positional construction (Point(x=1, y=2)), a readable repr, and value equality. You do not get ordering (<, >) or hashing yet — those are opt-in, covered below.

Continue this lesson on YourAIZone

The full lesson has the complete explanation, worked examples, common mistakes, tests with instant checking and an AI tutor. Start right away as a guest, or create an account to keep your progress.

In the full lesson

  • The complete explanation
  • Worked examples
  • Common mistakes and how to avoid them
  • Test questions: 15
  • AI tutor that answers questions about the topic
  • Progress tracking for students, parents and teachers