Dataclasses

Python 3.14 · ✓ verified by execution on 2026-07-26

Writing a Python class often requires a lot of repetitive boilerplate code. You have to define __init__ to assign arguments to attributes, __repr__ for readable printing, and __eq__ for value comparison.

Python’s dataclasses module provides a powerful decorator that automates this entire process. By simply adding @dataclass above your class definition and providing type hints for your fields, Python writes the tedious dunder methods for you.

Basic Usage

The @dataclass decorator reads your class variables and automatically generates an __init__ method and a clean __repr__ method.

python
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(10, 20)
print(p)
Output
Point(x=10, y=20)

Crucial detail: You must include type hints (like : int). The dataclass generator relies entirely on these annotations to determine which variables are actually fields. Without a type hint, the generator completely ignores the variable.

Value Equality

By default, standard Python classes compare object memory addresses (identity) when you use ==. Dataclasses are smarter: they automatically generate an __eq__ method that compares the object field-by-field.

python
from dataclasses import dataclass

@dataclass
class Product:
    name: str
    price: float

p1 = Product('Apple', 1.0)
p2 = Product('Apple', 1.0)
print(p1 == p2)
Output
True

Managing Mutable Defaults with field()

If you want a field to have a default value, you simply assign it: is_active: bool = True. However, if you attempt to use an empty list [] as a default, Python will raise a ValueError because mutable defaults cause dangerous shared-state bugs across all instances.

To safely use dynamic or mutable defaults (like a fresh list for every new instance), you must use the field() function and provide a default_factory.

python
from dataclasses import dataclass, field

@dataclass
class Team:
    name: str
    members: list = field(default_factory=list)

t1 = Team('Red')
t1.members.append('Alice')
t2 = Team('Blue')
print(f'{t1.name}: {t1.members}')
print(f'{t2.name}: {t2.members}')
Output
Red: ['Alice']
Blue: []

Immutability: Frozen Dataclasses

Sometimes you want an object to be read-only after it is created. You can achieve this by passing frozen=True to the decorator. Any attempt to modify a field on a frozen dataclass raises a FrozenInstanceError.

python
from dataclasses import dataclass

@dataclass(frozen=True)
class Location:
    lat: float
    lon: float

loc = Location(37.77, -122.41)
try:
    loc.lat = 0.0
except Exception as e:
    print(type(e).__name__)
Output
FrozenInstanceError

Because frozen dataclasses cannot be modified, Python can safely generate a __hash__ method for them (as long as all of their fields are also hashable). This allows you to use your dataclasses as dictionary keys or inside sets.

python
from dataclasses import dataclass

@dataclass(frozen=True)
class Location:
    name: str

# Because it is frozen and its fields are hashable, it can be a dict key
visited = {Location('Paris'): True}
print(visited[Location('Paris')])
Output
True

Note on Immutability: frozen=True only prevents reassignment of the object’s direct attributes (shallow immutability). If a field holds a mutable list, you can still append to that list!

Post-Initialization

Because the @dataclass decorator writes the __init__ method for you, you can’t put custom logic inside __init__ (like calculating a derived field). To solve this, dataclasses provide __post_init__, a special method that automatically runs immediately after the generated __init__ finishes.

python
from dataclasses import dataclass

@dataclass
class Rectangle:
    width: float
    height: float
    area: float = 0.0

    def __post_init__(self):
        self.area = self.width * self.height

rect = Rectangle(5.0, 4.0)
print(rect.area)
Output
20.0

Dataclasses drastically reduce visual clutter, keep your code DRY (Don’t Repeat Yourself), and represent the modern Pythonic standard for defining data-heavy structures.

Check yourself

Why must you provide type hints for fields when using `@dataclass`?

Reveal answer

The `@dataclass` decorator uses the type hints to know which variables belong to the class. — The `@dataclass` decorator relies entirely on PEP 526 variable annotations to know which fields belong to the class. If you leave out the type hint, the field is completely ignored by the dataclass generator.

What happens if you use an empty list `[]` as a default value for a field inside a dataclass?

Reveal answer

It raises a ValueError at class definition time to prevent the shared mutable default argument bug. — To protect you from the classic shared mutable state bug, `@dataclass` refuses to let you use `[]` or `{}` as default values. You must use `field(default_factory=list)` instead.

Are dataclasses faster than regular Python classes?

Reveal answer

No, they are standard Python classes with automatically generated boilerplate code. — Dataclasses are standard Python classes; they just have boilerplate code automatically written for them. They do not run any faster or slower than an equivalently written manual class.

What does `frozen=True` do to a dataclass?

Reveal answer

It prevents reassignment of the dataclass's own attributes, making it read-only. — Setting `frozen=True` prevents the reassignment of the dataclass's own attributes (shallow immutability). However, if a field contains a mutable object like a list, the contents of that list can still be modified.

Challenges

Challenge 1 +50 XP

Fix the bug! The `Playlist` dataclass tries to assign an empty list as the default value for `songs`. This raises a `ValueError` to prevent a shared-mutable-state bug. Fix the `songs` attribute by importing `field` from `dataclasses` and using `default_factory`.

python
  • Test 1 — expects "[]\n"
Need a hint? (−25% XP)

Change `songs: list = []` to `songs: list = field(default_factory=list)`. Don't forget to import `field`!

Show solution (0 XP)
from dataclasses import dataclass, field

@dataclass
class Playlist:
    name: str
    songs: list = field(default_factory=list)

p = Playlist('Workout')
print(p.songs)

Challenge 2 +100 XP

Create a `User` dataclass with three fields: `username` (string), `email` (string), and `is_active` (boolean, defaulting to `True`). Instantiate a user named 'admin' and print the object.

python
  • Test 1 — expects "User(username='admin', email='admin@example.com', is_active=True)\n"
Need a hint? (−25% XP)

Use `@dataclass`, then define `username: str`, `email: str`, and `is_active: bool = True`.

Show solution (0 XP)
from dataclasses import dataclass

@dataclass
class User:
    username: str
    email: str
    is_active: bool = True

user = User('admin', 'admin@example.com')
print(user)