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 thenamefield 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=Falseon 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
ClassVarandInitVarfieldsfrozensince 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.