I have two dataclasses that I need to add dynamicly generated attribute "code: a lower case version of the name" on both of them
for the first one I did
@dataclass() class FirstClass: name: str code: Optional[str] = field(init=False) def __post_init__(self): self.code = self.name.lower() with init=False I don't need to provide it in the constructor, since it's gonna be generated anyway
however second class is Frozen because I cache its return since it's too big and too expensive to read everytime
@dataclass(frozen=True) class SecondClass: name: str is there anyway to add dynamically generated attribute during init and not post_init because frozen dataclasses are read-only
so I want to do something like
@dataclass(frozen=True) class SecondClass: name: str code: str = name.lower() 3 Answers
One option could be to use object.__setattr__ to bypass the fact that the dataclass is frozen:
from dataclasses import dataclass, field @dataclass(frozen=True) class SecondClass: name: str code: 'str | None' = field(init=False) def __post_init__(self, ): object.__setattr__(self, 'code', self.name.lower()) print(SecondClass('Test')) Another option could be to add a helper class method new() which can be used to instantiate a new SecondClass object:
from dataclasses import dataclass @dataclass(frozen=True) class SecondClass: name: str code: 'str | None' @classmethod def new(cls, name: str): return cls(name, name.lower()) print(SecondClass.new('Hello')) 6Another approach, using @property - doesn't store code in the class but exposes it as an attribute:
@dataclass(frozen=True) class SecondClass: name: str @property def code(self): return self.name.lower() print(SecondClass('Me').code) Result:
me 1I'd use a combination of the first approach with a custom creation function:
@dataclass(frozen=True) class SecondClass: name: str code: Optional[str] def create_second(name): return SecondClass(name, name.lower()) print(create_second('Me')) Result:
SecondClass(name='Me', code='me') 1