frozen=True blocks field assignment but does not recursively freeze objects stored in those fields.
Python frozen dataclass: field rebinding stops, nested mutation remains
Operation contract
A dispatch plan stores route IDs in a list. The frozen dataclass refuses a new list assignment, yet a caller can append to the existing list. If plans must be stable snapshots, store a tuple built from the input instead. Dataclass replacement also reuses referenced objects unless the copy policy says otherwise.
Failure and ownership boundary
A frozen dataclass containing a list is not a safe dictionary key: hashing still reaches an unhashable mutable field. Do not use unsafe_hash to conceal that state. Create a new validated value object when the business action changes the plan, and avoid retaining a caller-owned mutable list.
Working program
from dataclasses import FrozenInstanceError, dataclass
@dataclass(frozen=True)
class DispatchPlan:
route_ids: list[str]
plan = DispatchPlan(["route-47"])
plan.route_ids.append("route-48")
print("routes:", plan.route_ids)
try:
plan.route_ids = []
except FrozenInstanceError:
print("field rebinding rejected")Output
routes: ['route-47', 'route-48']
field rebinding rejectedCosts and limits
Freezing field assignment has a small initialization cost but makes no deep copy. Converting n entries to a tuple costs O(n) time and storage.
Common Mistakes
- frozen=True does not mean deep immutability.
- Do not assume a frozen object containing a list is hashable.
- Do not use unsafe_hash to hide mutable key fields.
Connected lessons
Python dataclasses: frozen fields require an immutable value model, Python dataclasses.replace: a new record can retain old mutable fields, Python hash and equality: immutable dictionary keys.
