如何实现支持嵌套类继承的类注解父类(类似dataclass)并将数据存储到字典中
如何实现支持嵌套类继承的类注解父类(类似dataclass)并将数据存储到字典中
我明白你想要打造一个类似dataclass但支持多层继承,还把所有数据都存在字典里的父类。你当前的代码只能处理直接子类,嵌套继承就失效了——核心问题在于你只收集了当前子类的类型提示和默认值,完全没考虑父类层面的属性。咱们来把这个问题彻底解决:
问题根源分析
你的原代码在__init_subclass__里用get_type_hints(cls)只获取了当前类的属性提示,defaults也只从当前类的__dict__里提取。当定义NestedData这样的嵌套子类时,父类MyData的x、y、label这些属性根本没被纳入处理逻辑,初始化时自然会出问题。
修复后的完整代码
我们需要遍历类的方法解析顺序(MRO),递归收集所有父类(直到Data本身)的属性信息,合并后再用于初始化逻辑:
from typing import get_type_hints, FrozenDict class Data: metadata: dict def __init_subclass__(cls): # 收集所有父类(排除Data本身)的类型提示和默认值 all_hints = {} all_defaults = {} # 遍历MRO,从最上层父类到当前类,确保父类属性先被加载,子类可以覆盖 for base in reversed(cls.__mro__): if base is Data: break # 合并父类的类型提示 base_hints = get_type_hints(base) all_hints.update(base_hints) # 合并父类的默认值 base_defaults = {k: v for k, v in base.__dict__.items() if not k.startswith("__")} all_defaults.update(base_defaults) # 最后合并当前类的属性,确保子类属性覆盖父类同名属性 current_hints = get_type_hints(cls) all_hints.update(current_hints) current_defaults = {k: v for k, v in cls.__dict__.items() if not k.startswith("__")} all_defaults.update(current_defaults) def __init__(self, metadata=FrozenDict(), **kwargs): metadata = dict(metadata) # 处理所有收集到的属性 for key, typ in all_hints.items(): if key == "metadata": continue if key in kwargs: metadata[key] = kwargs[key] elif key in all_defaults: metadata[key] = all_defaults[key] else: raise TypeError(f"Missing required argument: '{key}'") self.metadata = metadata cls.__init__ = __init__ def __repr__(self): if not hasattr(self, "__cached_repr"): self.__cached_repr = f"{cls.__name__}(metadata={repr(self.metadata)})" return self.__cached_repr cls.__repr__ = __repr__ def __str__(self): if not hasattr(self, "__cached_str"): argstr = ",".join( f"{k}={repr(v)}" for k, v in self.metadata.items() if k in all_hints ) self.__cached_str = f"{cls.__name__}({argstr})" return self.__cached_str cls.__str__ = __str__
测试验证
现在你的示例代码就能正常运行了:
class MyData(Data): x: int y: float = 3.14 # 默认值 label: str = "default" class NestedData(MyData): extra: str = "nested" # ✅ 直接子类正常工作 n1 = MyData(x=10) print(n1) # 输出: MyData(x=10,y=3.14,label='default') print(n1.metadata) # 输出: {'x': 10, 'y': 3.14, 'label': 'default'} # ✅ 嵌套子类也正常工作了 n2 = NestedData(x=10) print(n2) # 输出: NestedData(x=10,y=3.14,label='default',extra='nested') print(n2.metadata) # 输出: {'x': 10, 'y': 3.14, 'label': 'default', 'extra': 'nested'}
额外说明
- 属性覆盖:如果子类定义了和父类同名的属性,子类的属性会优先生效(因为我们最后合并当前类的属性)
- 性能优化:
__init_subclass__只会在类定义时运行一次,所有属性收集逻辑都是一次性完成的,不会影响实例初始化的性能 - 默认值处理:如果不需要
FrozenDict,可以把默认的metadata参数换成普通dict(),注意要避免可变默认参数的陷阱(当前用FrozenDict是安全的)
备注:内容来源于stack exchange,提问作者Nick Skywalker
相关产品推荐
相关产品推荐

