What are dataclasses in Python?

The @dataclass decorator auto-generates __init__, __repr__, and __eq__ for a class based on its annotated fields - less boilerplate than writing them manually.

Python

Overview

Introduced in Python 3.7, `@dataclass` eliminates the boilerplate of writing `__init__`, `__repr__`, and `__eq__` for simple data-holding classes. You declare fields with type annotations and the decorator generates the methods for you.

Key Points

  • •`@dataclass` generates `__init__`, `__repr__`, and `__eq__` automatically.
  • •Fields with defaults come after fields without: `x: int = 0`.
  • •Use `field(default_factory=list)` for mutable defaults (never use `field: list = []`).
  • •`@dataclass(frozen=True)` makes instances immutable and hashable (like a named tuple).
  • •`@dataclass(order=True)` generates `<`, `<=`, `>`, `>=` comparisons.
  • •For complex defaults or validation, use `__post_init__`.

Examples

Basic dataclass

python
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
    label: str = ''   # field with default

p1 = Point(1.0, 2.0)
p2 = Point(1.0, 2.0)

print(p1)          # Point(x=1.0, y=2.0, label='')
print(p1 == p2)    # True  (generated __eq__)

# No need to write __init__, __repr__, or __eq__ manually

Mutable default with field()

python
from dataclasses import dataclass, field

@dataclass
class Inventory:
    name: str
    # WRONG: items: list = []  ← shared across all instances!
    items: list = field(default_factory=list)
    tags:  set  = field(default_factory=set)

inv = Inventory('Warehouse')
inv.items.append('Box')  # only this instance is affected

Frozen dataclass (immutable)

python
@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int

red = Color(255, 0, 0)
# red.r = 128  # FrozenInstanceError: cannot assign to field 'r'

# Frozen dataclasses are hashable, so they can be dict keys or set members
palette = {red, Color(0, 255, 0)}

__post_init__ for validation or computed fields

python
from dataclasses import dataclass, field

@dataclass
class Rectangle:
    width:  float
    height: float
    area:   float = field(init=False)  # not a constructor parameter

    def __post_init__(self):
        if self.width <= 0 or self.height <= 0:
            raise ValueError("Dimensions must be positive")
        self.area = self.width * self.height

r = Rectangle(4.0, 5.0)
print(r.area)  # 20.0

Pro Tip

Prefer `@dataclass` over plain classes for data containers. For read-only data, use `frozen=True` - it prevents accidental mutation and makes instances hashable. For very simple tuples of values, `typing.NamedTuple` is an alternative; for full validation, look at `pydantic`.