Skip to content

Dataclasses

A class that holds data typically needs __init__, __repr__, and __eq__ — all written by hand with identical logic: copy constructor arguments into self.attr, format attributes into a string, compare attribute by attribute. @dataclass generates all of this from the class-level type annotations.

from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float

Python generates:

  • __init__(self, x: float, y: float) that sets self.x and self.y
  • __repr__ that returns "Point(x=1.0, y=2.0)"
  • __eq__ that compares all fields

Scalar defaults work like keyword arguments:

@dataclass
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False

You cannot use a mutable object (list, dict) directly as a default — Python would share one instance across all objects. Use field(default_factory=...) instead:

from dataclasses import dataclass, field
@dataclass
class Inventory:
name: str
tags: list = field(default_factory=list)
quantity: int = 0

field also accepts:

ParameterEffect
default_factoryCalled with no arguments to produce each instance’s default
repr=FalseExclude the field from __repr__
compare=FalseExclude the field from __eq__ and __lt__
init=FalseExclude from __init__; set manually in __post_init__

frozen=True makes the dataclass immutable: it generates __setattr__ and __delattr__ that raise FrozenInstanceError, and it also generates __hash__ so frozen instances can be used as dict keys or in sets.

@dataclass(frozen=True)
class Color:
red: int
green: int
blue: int

A dataclass is still a normal class — add any methods you need:

@dataclass
class Point:
x: float
y: float
def distance_from_origin(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float
def distance_from_origin(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
@dataclass
class Inventory:
name: str
tags: list = field(default_factory=list)
quantity: int = 0
def add_tag(self, tag: str) -> None:
self.tags.append(tag)
@dataclass(frozen=True)
class Color:
red: int
green: int
blue: int
p = Point(3.0, 4.0)
print(p)
print(p.distance_from_origin())
item = Inventory("Widget")
item.add_tag("sale")
item.add_tag("clearance")
print(item)
red = Color(255, 0, 0)
print(red)
print(red == Color(255, 0, 0))
Which dunder methods does `@dataclass` generate automatically?
Why can't you write `tags: list = []` as a dataclass field default?
What does `frozen=True` add to a dataclass?
What is `field(repr=False)` used for?