简体   繁体   中英

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM