使用Python Dataclasses实现多继承时__post_init__执行异常求助
首先咱们来拆解你遇到的问题:
问题根源:多继承MRO与调用链断裂
你的类继承顺序是NamedAndNumbered(NumberedObj, NamedObj),Python的方法解析顺序(MRO)是:NamedAndNumbered -> NumberedObj -> NamedObj -> object。
当你在子类的__post_init__里调用super().__post_init__()时,只会触发MRO链里下一个类的__post_init__(也就是NumberedObj的),但NumberedObj的__post_init__没有继续调用super(),导致NamedObj的初始化逻辑完全没机会执行。
至于你尝试super(NamedObj, self).__post_init__()报错,是因为NamedObj在MRO中的下一个类是object,而object根本没有__post_init__方法,自然会抛出AttributeError。
解决方案:构建完整的super调用链
要让所有父类的__post_init__都执行,核心是让每个父类的__post_init__都主动调用super().__post_init__(),把调用沿着MRO链传递下去。同时为了避免最后调用object时的报错,咱们可以加个简单的异常处理或存在性检查。
修改后的代码如下:
from dataclasses import dataclass @dataclass class NamedObj: name: str def __post_init__(self): print("NamedObj __post_init__") self.name = "Name: " + self.name # 传递调用,若下一个类无__post_init__则忽略 try: super().__post_init__() except AttributeError: pass @dataclass class NumberedObj: number: int = 0 def __post_init__(self): print("NumberedObj __post_init__") self.number += 1 # 继续传递调用链 try: super().__post_init__() except AttributeError: pass @dataclass class NamedAndNumbered(NumberedObj, NamedObj): def __post_init__(self): super().__post_init__() print("NamedAndNumbered __post_init__")
现在实例化NamedAndNumbered('n_and_n'),输出会是:
NumberedObj __post_init__ NamedObj __post_init__ NamedAndNumbered __post_init__
更优雅的写法(可选)
如果你不想用try-except,也可以通过hasattr检查super对象是否存在__post_init__方法:
# 替换每个父类__post_init__末尾的try-except为: if hasattr(super(), '__post_init__'): super().__post_init__()
原理说明
当子类调用super().__post_init__()时,会触发NumberedObj的初始化逻辑;NumberedObj里的super()会沿着MRO找到NamedObj的__post_init__;最后NamedObj的super()尝试调用object的方法,此时因为object没有这个方法,我们的容错处理会跳过这个调用,不会报错。整个调用链完整覆盖了所有父类的逻辑,同时符合Python多继承的设计原则。
内容的提问来源于stack exchange,提问作者Chris J Harris

