Search code examples
pythonpython-3.xpython-dataclasses

how to add values for frozen dataclasses during initialization


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

Solution

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