Home > database >  how to add values for frozen dataclasses during initialization
how to add values for frozen dataclasses during initialization

Time:09-15

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()

CodePudding user response:

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'))

CodePudding user response:

Another 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

CodePudding user response:

I'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')
  • Related