[Python-Dev] Should the dataclass frozen property apply to subclasses?

Eric V. Smith eric at trueblade.com
Thu Feb 22 05:55:07 EST 2018


On 2/22/2018 1:56 AM, Raymond Hettinger wrote:
> When working on the docs for dataclasses, something unexpected came up.  If a dataclass is specified to be frozen, that characteristic is inherited by subclasses which prevents them from assigning additional attributes:
> 
>      >>> @dataclass(frozen=True)
>      class D:
>              x: int = 10
> 
>      >>> class S(D):
>              pass
> 
>      >>> s = S()
>      >>> s.cached = True
>      Traceback (most recent call last):
>        File "<pyshell#49>", line 1, in <module>
>          s.cached = True
>        File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py", line 448, in _frozen_setattr
>          raise FrozenInstanceError(f'cannot assign to field {name!r}')
>      dataclasses.FrozenInstanceError: cannot assign to field 'cached'

This is because "frozen-ness" is implemented by adding __setattr__ and 
__delattr__ methods in D, which get inherited by S.

> Other immutable classes in Python don't behave the same way:
> 
> 
>      >>> class T(tuple):
>              pass
> 
>      >>> t = T([10, 20, 30])
>      >>> t.cached = True
> 
>      >>> class F(frozenset):
>              pass
> 
>      >>> f = F([10, 20, 30])
>      >>> f.cached = True
> 
>      >>> class B(bytes):
>              pass
> 
>      >>> b = B()
>      >>> b.cached = True

The only way I can think of emulating this is checking in __setattr__ to 
see if the field name is a field of the frozen class, and only raising 
an error in that case.

A related issue is that dataclasses derived from frozen dataclasses are 
automatically "promoted" to being frozen.

 >>> @dataclass(frozen=True)
... class A:
...     i: int
...
 >>> @dataclass
... class B(A):
...     j: int
...
 >>> b = B(1, 2)
 >>> b.j = 3
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "C:\home\eric\local\python\cpython\lib\dataclasses.py", line 
452, in _frozen_setattr
     raise FrozenInstanceError(f'cannot assign to field {name!r}')
dataclasses.FrozenInstanceError: cannot assign to field 'j'

Maybe it should be an error to declare B as non-frozen?

Eric.



More information about the Python-Dev mailing list