如何子类化冻结数据类?实例化SpecialCustomer遇FrozenInstanceError
我继承了Customer数据类,它用于标识客户数据库表中的客户。Customer用于生成特定客户的交易汇总统计信息,它是可哈希的,因此被设置为冻结状态。
我需要一个Customer的子类SpecialCustomer,它具备额外属性special_property。从Customer继承的多数属性将被设为固定值,该客户不存在于客户表中。我希望复用为Customer编写的代码,但缺少special_property将无法区分不同的特殊客户。
请问如何实例化SpecialCustomer?以下是我的实现代码,我知道它无法运行的原因,是否有可行的解决方法?
from dataclasses import dataclass @dataclass(frozen=True, order=True) class Customer: property: str @dataclass(frozen=True, order=True) class SpecialCustomer(Customer): special_property: str = Field(init=False) def __init__(self, special_property): super().__init__(property="dummy_value") self.special_property = special_property s = SpecialCustomer(special_property="foo")
报错信息:
E dataclasses.FrozenInstanceError: cannot assign to field 'special_property' <string>:4: FrozenInstanceError
冻结数据类(frozen=True)初始化完成后不允许修改字段,直接在__init__里赋值self.special_property会触发报错。以下是几种可行的解决方式:
方法一:规范利用dataclass字段默认值
既然SpecialCustomer继承的property是固定值,直接给父类的字段设置默认值,同时声明special_property为必填字段,dataclass会自动生成正确的初始化逻辑,无需手动编写__init__:
from dataclasses import dataclass @dataclass(frozen=True, order=True) class Customer: property: str @dataclass(frozen=True, order=True) class SpecialCustomer(Customer): # 给父类的property设置固定默认值 property: str = "dummy_value" # 声明特殊属性为必填字段 special_property: str # 直接实例化即可 s = SpecialCustomer(special_property="foo") print(s.property) # 输出: dummy_value print(s.special_property) # 输出: foo
这种方式符合dataclass的设计规范,代码简洁且不会触发冻结类的赋值限制,是最优方案。
方法二:在自定义__init__中绕过冻结限制
如果必须保留自定义__init__逻辑,可以通过调用object.__setattr__绕过冻结类的属性赋值限制:
from dataclasses import dataclass, Field @dataclass(frozen=True, order=True) class Customer: property: str @dataclass(frozen=True, order=True) class SpecialCustomer(Customer): special_property: str = Field(init=False) def __init__(self, special_property): super().__init__(property="dummy_value") # 调用object的__setattr__绕过冻结限制 object.__setattr__(self, 'special_property', special_property) s = SpecialCustomer(special_property="foo") print(s.special_property) # 输出: foo
这种方式虽然可行,但破坏了dataclass的自动初始化逻辑,仅在特殊场景下使用。
方法三:利用__post_init__阶段赋值
在__post_init__方法中调用object.__setattr__赋值,这是dataclass预留的初始化后处理阶段:
from dataclasses import dataclass, Field @dataclass(frozen=True, order=True) class Customer: property: str @dataclass(frozen=True, order=True) class SpecialCustomer(Customer): special_property: str = Field(init=False) # 临时接收参数的字段 _special_prop: str def __post_init__(self): object.__setattr__(self, 'special_property', self._special_prop) # 可选:删除临时字段 object.__delattr__(self, '_special_prop') def __init__(self, special_property): super().__init__(property="dummy_value") object.__setattr__(self, '_special_prop', special_property) s = SpecialCustomer(special_property="foo") print(s.special_property) # 输出: foo
这种方式适合需要复杂初始化逻辑的场景,但代码相对繁琐。
内容的提问来源于stack exchange,提问作者GlaceCelery

