Dataclasses
Dataclasses
Section titled βDataclassesβWhat it is
Section titled βWhat it isβ@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.
Before this feature
Section titled βBefore this featureβ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) # TrueAfter this feature
Section titled βAfter this featureβfrom dataclasses import dataclass
@dataclassclass Point: x: int y: int
p1 = Point(1, 2)p2 = Point(1, 2)print(p1) # Point(x=1, y=2) -- __repr__ generatedprint(p1 == p2) # True -- __eq__ generatedfrom dataclasses import dataclass, field
@dataclassclass 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.FrozenInstanceErrorWhy this is better
Section titled βWhy this is betterβ- 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=Truefor immutability,order=Trueto generate comparison operators,field(default_factory=...)for safe mutable defaults.
Key notes / edge cases
Section titled βKey notes / edge casesβ- Mutable defaults (
items: list = []) raise aValueErrorat class-definition time β usefield(default_factory=list)instead, exactly because plaindef __init__(self, items=[])would silently share one list across instances. @dataclassgenerates__eq__by default but not__hash__unlessfrozen=Trueor you seteq=Falseβ a mutable dataclass with__eq__is unhashable, same rule as any class where you define__eq__without__hash__.field(repr=False)orfield(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.
@dataclassclass 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 ** 2Quick practice
Section titled βQuick practiceβ-
What three methods does
@dataclassgenerate by default?Answer
`__init__`, `__repr__`, and `__eq__`, based on the class's annotated fields. -
Why canβt you write
items: list = []as a dataclass field default?Answer
A 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. -
How do you make a dataclass immutable?
Answer
`@dataclass(frozen=True)` β attribute assignment after construction then raises `FrozenInstanceError`.