Serialisation and copying are what you do with data objects most, and the helpers have important subtleties.
The module provides helpers that work on any dataclass instance:
asdict(obj)returns a dict of field names to values, recursively converting nested dataclasses, and copying lists, tuples and dicts withcopy.deepcopyfor other values. Perfect forjson.dumps(asdict(obj)), as long as the values are JSON-serialisable (dates and Decimals needdefault=str).astuple(obj)does the same into a tuple.dict_factory=customises the result, e.g. droppingNonevalues.replace(obj, **changes)creates a new instance through__init__, so__post_init__and validation run again. Fields withinit=Falsecan't be passed and are recomputed.fields(obj)lists the fields;is_dataclass(x)checks for a dataclass class or instance.
asdict is convenient but not free: it deep-copies, which is slow for large objects. For hot paths, a hand-written {"a": self.a, ...} or vars(obj) (shallow, includes only instance attributes, not for slotted classes) is much faster.
The reverse direction, dict to dataclass, is MyClass(**data) for flat data; nested structures need your own code or a library such as cattrs, dacite or Pydantic.
Example
import json
from dataclasses import asdict, astuple, dataclass, field, replace
@dataclass
class Address:
city: str
zip: str | None = None
@dataclass
class User:
name: str
address: Address
roles: list[str] = field(default_factory=list)
u = User("ada", Address("London"), ["admin"])
print(asdict(u))
# {'name': 'ada', 'address': {'city': 'London', 'zip': None}, 'roles': ['admin']}
print(astuple(u)) # ('ada', ('London', None), ['admin'])
no_none = asdict(u.address, dict_factory=lambda kv: {k: v for k, v in kv if v is not None})
print(json.dumps(no_none)) # {"city": "London"}
moved = replace(u, address=Address("Paris", "75001"))
print(moved.address.city, u.address.city) # Paris London
print(User(**{"name": "bob", "address": Address("Rome")}))Remember
- asdict/astuple convert recursively, deep-copying values
- replace() re-runs __init__ and __post_init__
- Dict to nested dataclass needs custom code or a library
Watch out for
- Calling asdict on large objects in a hot loop; its deep copy is expensive.