Frozen dataclasses

dataclasses · Essentials · 8 min

Immutable values are safe to share, cache and use as dict keys or set members.

@dataclass(frozen=True) makes instances read-only after __init__: assigning or deleting an attribute raises dataclasses.FrozenInstanceError (a subclass of AttributeError).

Benefits:

  • Instances can't be changed behind your back when shared between functions or threads.
  • With eq=True (the default) and frozen=True, a __hash__ is generated from the fields, so instances work as dict keys and set members.
  • They signal intent: this is a value, like a date or a coordinate.

To "change" a frozen instance, make a new one: dataclasses.replace(obj, field=new_value) copies all fields and overrides the given ones. Python 3.13 also adds the generic copy.replace(obj, ...), which works for dataclasses, named tuples and other supporting types.

Two caveats:

  • Frozen is shallow: a list field can still be mutated in place. Use tuples or frozenset for truly immutable data.
  • Inside __post_init__, normal assignment is blocked too. To set a derived field there, use object.__setattr__(self, "name", value), the documented escape hatch.

Frozen instances are slightly slower to create, since __init__ uses object.__setattr__.

Example

import copy
from dataclasses import dataclass, replace, FrozenInstanceError

@dataclass(frozen=True)
class Money:
    amount: int
    currency: str = "EUR"

price = Money(500)
try:
    price.amount = 600
except FrozenInstanceError as e:
    print("blocked:", e)                    # blocked: cannot assign to field 'amount'

discounted = replace(price, amount=450)
print(price, discounted)                    # Money(amount=500, currency='EUR') Money(amount=450, currency='EUR')
print(copy.replace(price, currency="USD"))  # Money(amount=500, currency='USD')  (3.13)

totals = {Money(500): "five euros"}         # hashable: usable as a dict key
print(totals[Money(500)])                   # five euros

Remember

  • frozen=True blocks attribute assignment after __init__
  • Frozen + eq makes instances hashable
  • replace() and copy.replace() (3.13) create modified copies

Watch out for

  • Assuming frozen means deeply immutable; list fields can still be changed in place.

Check yourself

  1. How do you get a copy of a frozen dataclass instance with one field changed?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/dataclasses.html#frozen-instances