Skip to content

Dataclasses

@dataclass (from the dataclasses module, Python 3.7+) is a class decorator that auto-generates the boilerplate for classes that mostly just hold data: __init__, __repr__, and __eq__, based on type-annotated class attributes.

class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r})"
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1) # Point(x=1, y=2)
print(p1 == p2) # True
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1) # Point(x=1, y=2) -- __repr__ generated
print(p1 == p2) # True -- __eq__ generated
from dataclasses import dataclass, field
@dataclass
class Order:
id: int
items: list = field(default_factory=list) # safe mutable default
total: float = 0.0
_internal_note: str = field(default="", repr=False) # excluded from __repr__
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
ip = ImmutablePoint(1, 2)
ip.x = 5 # raises dataclasses.FrozenInstanceError
  • Less boilerplate: __init__, __repr__, __eq__ written for you from the field annotations.
  • Type-annotation driven: fields double as documentation and (with a type checker) real static checking.
  • Configurable: frozen=True for immutability, order=True to generate comparison operators, field(default_factory=...) for safe mutable defaults.
  • Mutable defaults (items: list = []) raise a ValueError at class-definition time β€” use field(default_factory=list) instead, exactly because plain def __init__(self, items=[]) would silently share one list across instances.
  • @dataclass generates __eq__ by default but not __hash__ unless frozen=True or you set eq=False β€” a mutable dataclass with __eq__ is unhashable, same rule as any class where you define __eq__ without __hash__.
  • field(repr=False) or field(compare=False) exclude a field from the generated __repr__/__eq__ without removing it from __init__.
  • Dataclasses are still plain classes β€” you can add your own methods, properties, and __post_init__ for validation after the generated __init__ runs.
@dataclass
class Circle:
radius: float
def __post_init__(self):
if self.radius <= 0:
raise ValueError("radius must be positive")
@property
def area(self):
return 3.14159 * self.radius ** 2
  1. What three methods does @dataclass generate by default?

    Answer`__init__`, `__repr__`, and `__eq__`, based on the class's annotated fields.
  2. Why can’t you write items: list = [] as a dataclass field default?

    AnswerA mutable default would be shared across every instance (the same bug as `def f(x=[])`); dataclasses reject it outright and require `field(default_factory=list)` instead.
  3. How do you make a dataclass immutable?

    Answer`@dataclass(frozen=True)` β€” attribute assignment after construction then raises `FrozenInstanceError`.