Understanding the generated code explains performance, pattern matching, and how dataclass-like libraries work.
@dataclass runs once, at class creation. It reads the class's __annotations__ in order, builds a Field object for each and stores them in __dataclass_fields__ (with options in __dataclass_params__). Then it generates source code for methods such as __init__, __repr__ and __eq__ as text, compiles it with exec, and attaches the functions to the class, unless the class already defines them (for __init__, __repr__, __eq__) or raises TypeError on conflict (ordering methods with order=True).
So the generated __init__ is ordinary Python, as fast as one you'd write by hand. There's no per-instance overhead beyond normal attribute storage; the cost is paid once at import.
Other generated pieces:
__match_args__: a tuple of positional field names, enablingcase Point(x, y):in structural pattern matching (match_args=Falseturns it off).__slots__or__weakref__with the corresponding options.__replace__(3.13), used bycopy.replace().
make_dataclass("Point", [("x", float), ("y", float, 0.0)]) builds a dataclass dynamically, useful when the fields come from a schema.
Descriptor-typed fields: if a field's default is a descriptor (with __get__/__set__), __init__ assigns through it, which lets you add per-field validation or conversion while keeping dataclass ergonomics.
Example
import inspect
from dataclasses import dataclass, make_dataclass
@dataclass
class Point:
x: float
y: float = 0.0
print(list(Point.__dataclass_fields__)) # ['x', 'y']
print(inspect.signature(Point)) # (x: float, y: float = 0.0) -> None
print(Point.__match_args__) # ('x', 'y')
print(Point.__init__.__qualname__) # Point.__init__ (generated with exec)
def describe(obj):
match obj:
case Point(0, 0): return "origin"
case Point(x, 0): return f"on the x-axis at {x}"
case Point(): return "somewhere"
print(describe(Point(0, 0)), "|", describe(Point(5))) # origin | on the x-axis at 5
Color = make_dataclass("Color", [("r", int), ("g", int), ("b", int, 0)], frozen=True)
print(Color(255, 128), hasattr(Color, "__replace__")) # Color(r=255, g=128, b=0) TrueRemember
- The decorator stores Field objects in __dataclass_fields__ and exec-generates methods
- Generated methods are plain Python: cost is paid once at class creation
- __match_args__ enables positional patterns; make_dataclass builds classes dynamically
Watch out for
- Expecting runtime type checks from the generated __init__; it only assigns values.