Lib/dataclasses.py
Source:
cpython 3.14 @ ab2d84fe1023/Lib/dataclasses.py
Map
| Lines | Symbol | Purpose |
|---|---|---|
| 1–80 | module header, __all__ | Imports, sentinel objects, KW_ONLY marker |
| 81–260 | Field, field() | Field descriptor and factory function |
| 261–400 | _process_class | Core decorator logic: collect fields, call generators |
| 401–480 | _init_fn | Generate __init__ source, compile and exec it |
| 481–530 | _repr_fn | Generate __repr__ via compile+exec |
| 531–570 | __post_init__ insertion | Append call inside generated __init__ when defined |
| 571–640 | _frozen_setattr, _frozen_delattr | Raise FrozenInstanceError for frozen classes |
| 641–720 | @dataclass decorator | Entry point, delegates to _process_class |
| 721–820 | fields(), asdict(), astuple() | Public introspection helpers |
| 821–900 | replace() | Shallow copy with field overrides |
| 901–1000 | make_dataclass() | Dynamic class construction |
| 1001–1500 | _DataclassParams, slots, __match_args__ | Params container, __slots__ rewriting, pattern matching support |
Reading
@dataclass decorator and _process_class
The public @dataclass decorator is a thin wrapper. When called with arguments it returns a decorator; when called directly on a class it calls _process_class immediately.
_process_class walks the class MRO to collect Field objects, then calls the individual generator functions to attach __init__, __repr__, __eq__, and optionally __lt__/__hash__. The result is the original class, mutated in place.
# CPython: Lib/dataclasses.py:1049 _process_class
def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
match_args, kw_only, slots, weakref_slot):
fields = {}
...
for name, type in fields.items():
...
if init:
flds = [f for f in fields.values() if f.init]
_set_new_attribute(cls, '__init__',
_init_fn(flds, frozen, has_post_init,
self_name, globals, slots))
The function uses _set_new_attribute so that user-defined methods are never overwritten.
Field, field(), and the compile+exec pattern
Field is not intended to be constructed directly. The field() factory validates arguments and returns a Field instance. Fields with default_factory are forbidden from also having a default.
The most distinctive implementation detail in this file is how __init__ is synthesised. Rather than building a function object via closures, _init_fn assembles Python source as a string, then calls compile() and exec() on it. This approach lets the generated function have the correct signature visible to inspect.signature() and avoids the overhead of repeated getattr calls at runtime.
# CPython: Lib/dataclasses.py:491 _init_fn
def _init_fn(fields, frozen, has_post_init, self_name, globals, slots):
...
body_lines = []
for f in fields:
...
return _create_fn('__init__',
[self_name] + [_init_param(f) for f in fields],
body_lines,
locals=locals,
globals=globals,
return_type=None)
_create_fn is the central helper that renders the source string, calls compile, execs into a fresh namespace, and returns the resulting function object. The same pattern is reused for _repr_fn, _eq_fn, and the comparison methods.
Frozen classes and replace()
When frozen=True, _process_class installs _frozen_setattr and _frozen_delattr as __setattr__ and __delattr__. These unconditionally raise FrozenInstanceError. The generated __init__ must therefore use object.__setattr__ directly to set initial values.
# CPython: Lib/dataclasses.py:601 _frozen_setattr
def _frozen_setattr(self, name, value):
# In a frozen class, disallow setting attributes.
if type(self)._is_protocol:
...
raise FrozenInstanceError('cannot assign to field ' + repr(name))
replace() creates a shallow copy by calling copy.copy() and then applying overrides with object.__setattr__ when the class is frozen, or ordinary attribute assignment otherwise. Fields marked init=False cannot be overridden via replace() and raise TypeError if the caller tries.
gopy notes
Status: not yet ported.
Planned package path: module/dataclasses/.
The compile+exec code-generation pattern has no direct equivalent in Go. The gopy port will pre-generate __init__ bytecode through the compiler pipeline rather than assembling source strings at runtime. Field metadata will map to a Go struct mirroring the CPython Field object slots. replace() depends on copy.copy(), which in turn requires the object protocol (specifically __copy__), so module/copy/ must be available before this module can pass its test suite.