Skip to content
FollowtheDamnCode

Dataclasses

Data-holding classes without the boilerplate.

4 minute read

Big idea

A dataclass is a class mostly for holding data. Add @dataclass above it, list the fields with their types, and Python writes __init__, __repr__, and __eq__ for you.

Before and after

A plain class for a book needs a lot of typing:

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages

The dataclass version says the same thing — and more:

A dataclass
from dataclasses import dataclass

@dataclass
class Book:
    title: str
    author: str
    pages: int

b = Book("Dune", "Frank Herbert", 412)
print(b)
print(b.pages)
print(b == Book("Dune", "Frank Herbert", 412))
output
Book(title='Dune', author='Frank Herbert', pages=412)
412
True

Run and edit it in the app.

  • title: str — a field with a type hint. The hint documents the type; Python doesn't enforce it.
  • You get a readable print and == that compares field by field — for free.

Defaults and methods

Fields can have defaults (after the ones without), and a dataclass is still a class — add methods as usual:

Predict: a dataclass with a method
from dataclasses import dataclass

@dataclass
class Item:
    name: str
    price: float
    quantity: int = 1

    def total(self):
        return self.price * self.quantity

cart = [Item("pen", 1.5, 4), Item("book", 12.0)]
for item in cart:
    print(item.name, item.total())
print(sum(item.total() for item in cart))
What does it print? Show the answer
pen 6.0
book 12.0
18.0

Run and edit it in the app.

A list default needs a factory

As with function defaults, a list default would be shared between objects. Dataclasses refuse it outright, and give you field(default_factory=list) instead:

Fix the bug: each team needs its own list of members
from dataclasses import dataclass

@dataclass
class Team:
    name: str
    members: list = []

t = Team("Blue")
t.members.append("Ada")
print(t)
output
Team(name='Blue', members=['Ada'])

Run and edit it in the app.

The error is ValueError: mutable default <class 'list'> for field members is not allowed. Import field too, and write members: list = field(default_factory=list) — a fresh empty list for every team.

Sorting dataclasses

order=True adds <, > and friends, comparing fields in order — so a list of them can be sorted directly. More often you'll sort with a key:

books.sort(key=lambda b: b.pages)

Which methods does @dataclass write for you by default?

Watch it

Think of it like

A form with labelled boxes. You design the form once — Name, Price, Quantity — and every copy you fill in prints neatly and can be compared box by box.

Key terms

  • Dataclass — A class whose __init__, __repr__, and __eq__ are generated from its fields.
  • Field — One named piece of data in a dataclass.
  • Type hint — name: str; documents the expected type.

Check yourself

What does @dataclass generate?

Does a type hint like pages: int stop you storing a string?

How do you give a dataclass field an empty-list default?