it is common in python to make some of our class fields read only after initialization commonly by using
the property
decorator
class Person:
def __init__(self, name: str, age: int) -> None:
self._name = name
self.age = age
@property
def name(self) -> str:
return name
but writing many class by hand is tedious, so we switch to dataclasses
but the problem with dataclasses
is that they don’t support specific frozen field, in dataclasses
its all frozen or nothing is frozen, because we can define frozen
only on the class level
@dataclass(frozen=True) # makes all fields in class frozen
class Person:
name: str
age: int
if we want to freeze specific fields in our class using dataclass
we need to write a bit of code while the whole point of dataclasses
is to make it easy to write classes, but EVEN THEN the fields won’t be safe from manipulation!
@dataclass(repr=True)
class Person:
name: InitVar[str]
_name: str = field(init=False)
age: int
@property
def name_(self) -> str:
self._name
def __post_init__(self, name: str) -> None:
self._name = name
a lot of name
variables, hard to read and remember that this is only for 1 field
- field
name
- the init variable when people initilize the class they will writePerson(name=...,...)
- field
_name
- the actual field that will store the value, we mark it as private - property
name_
- a way for people to get the fieldname
, but we had to suffix it with_
so it won’t collide with thename
field annotation
ugh… so much code for a simple functionality, and even after all of that, someone can just come and manipulate the field
p = Person(name=..., age=...)
p._name = "foo"
like kw_only
can be define on the class level or per-field, it make sense to do it for frozen, since freezing specific fields is common.
this is how it will look like if we were able to freeze specific fields
@dataclass(repr=True)
class Person:
name: str = field(frozen=True)
age: int
much cleaner, more readable and simpler then the current solution we have.
also they are actually safe from manipulation
p = Person(name=..., age=....) p.name = "foo"
dataclass_test.FrozenInstanceError: cannot assign to field 'name'
limitations
now because frozen
is per field, it make sense to put to it some limitations where is doesn’t make sense using frozen
shouldn’t be possible to set field
frozen=False
on a frozen class@dataclass(frozen=True) class Person: name: str age: int = field(frozen=False) # should raise error
ValueError: field `name` marked as not frozen on a frozen class `Person`
dosn’t make sense to make
ClassVar
andInitVar
fieldsfrozen
since they are not attached to the running instance, THIS IS NOT BACKWARD COMPATIBLE@dataclass class Foo: x: InitVar[int] = field(frozen=True)
TypeError: field `x` cannot be frozen
please let me know if this make sense and if it should be implemented differently, I think a lot of people actually want it.