Search code examples
pythonpython-3.ximmutabilitynamedtuplepython-dataclasses

python frozen dataclass immutable with object.__setattr__


I used namedtuples for immutable data structures until I came across dataclasses, which I prefer in my use-cases (not relevant to the question).

Now I learned that they are not immutable! At least not strictly speaking.

setattr(frozen_dc_obj, "prop", "value") raises an exception. ok.
But why does object.__setattr__(frozen_dc_obj,..) work?

Compared to namedtuple, where it raises an exception!

from collections import namedtuple
from dataclasses import dataclass


NTTest = namedtuple("NTTest", "id")

nttest = NTTest(1)
setattr(nttest, "id", 2)  # Exception
object.__setattr__(nttest, "id", 2)  # Exception


@dataclass(frozen=True)
class DCTest:
    id: int

dctest = DCTest(1)
setattr(dctest, "id", 2)  # Exception
object.__setattr__(dctest, "id", 2)  # WORKS


Solution

  • namedtuple defines __slots__ = () and hence you can't set any attribute (it doesn't have a __dict__).

    Frozen dataclasses on the other hand perform a manual check in their __setattr__ method and raise an exception if it's a frozen instance.

    Compare the following:

    >>> class Foo:
    ...     __slots__ = ()
    ... 
    >>> f = Foo()
    >>> f.__dict__  # doesn't exist, so object.__setattr__ won't work
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Foo' object has no attribute '__dict__'
    >>> @dataclass(frozen=True)
    ... class Bar:
    ...     pass
    ... 
    >>> b = Bar()
    >>> b.__dict__  # this exists, so object.__setattr__ works
    {}