You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多继承中调用双亲构造函数的通用方法及问题解决

Python多继承中构造函数的正确调用方案

错误原因解析

你遇到的TypeError本质是Python**方法解析顺序(MRO)**在多继承场景下的连锁效应:
当Amfibii继承Car和Boat时,它的MRO链是Amfibii → Car → Boat → Vehicle → object。在Car.__init__中调用super().__init__(name)时,super()并不指向Vehicle,而是MRO链中的下一个类——Boat。但Boat.__init__需要n_props参数,仅传递name自然会触发参数缺失错误。

另外,原代码中Amfibii直接调用Car.__init__和Boat.__init__还会导致Vehicle.__init__被执行两次(两个父类各自调用),这也是多继承中需要避免的问题。

构造/析构函数的调用顺序

  • 构造函数:遵循MRO链的正序执行,即从最子类开始,依次向上调用每个父类的构造方法(正确使用super时)。
  • 析构函数:遵循MRO链的逆序执行,即从最顶层父类开始,依次向下调用每个子类的析构方法(Python自动处理,无需手动控制)。

正确实现方案:基于super的通用参数传递

核心思路是让每个类的构造函数通过关键字参数接收并传递额外参数,适配MRO链的调用逻辑:

class Vehicle:
    def __init__(self, name: str, **kwargs) -> None:
        self.name = name
        print(f'Creating a Vehicle: {name}')
        # 传递剩余参数给MRO链的下一个类(多继承场景必备)
        super().__init__(**kwargs)

    def __del__(self):
        print(f'Deleting a Vehicle: {self.name}')


class Car(Vehicle):
    def __init__(self, name: str, n_wheels: int, **kwargs) -> None:
        self.wheels = n_wheels
        print(f'Creating a Car: {name}')
        # 传递name和剩余参数给父类
        super().__init__(name, **kwargs)

    def __del__(self):
        print(f'Deleting a Car: {self.name}')
        super().__del__()


class Boat(Vehicle):
    def __init__(self, name: str, n_props: int, **kwargs) -> None:
        self.propellers = n_props
        print(f'Creating a Boat: {name}')
        # 传递name和剩余参数给父类
        super().__init__(name, **kwargs)

    def __del__(self):
        print(f'Deleting a Boat: {self.name}')
        super().__del__()


class Amfibii(Car, Boat):
    def __init__(self, name: str, n_wheels: int, n_props: int) -> None:
        print(f'Creating an Amfibii: {name}')
        # 通过关键字参数传递所有父类需要的参数
        super().__init__(name, n_wheels=n_wheels, n_props=n_props)

    def __del__(self):
        print(f'Deleting an Amfibii: {self.name}')
        super().__del__()


my_vehicle = Amfibii('Mazda', 4, 2)

代码说明

  1. **kwargs参数:每个类的构造函数都保留**kwargs,用于接收并传递当前类不需要的参数给MRO链的下一个类,避免参数传递中断。
  2. super()的正确使用:所有构造/析构函数都通过super()调用链上的下一个方法,确保MRO顺序被严格遵循,同时避免重复初始化父类。
  3. 关键字参数传递:在Amfibii中通过关键字参数传递所有父类需要的参数,确保每个父类能提取到自己所需的参数。

运行上述代码的输出如下:

Creating an Amfibii: Mazda
Creating a Car: Mazda
Creating a Boat: Mazda
Creating a Vehicle: Mazda
Deleting an Amfibii: Mazda
Deleting a Boat: Mazda
Deleting a Car: Mazda
Deleting a Vehicle: Mazda

备选方案:直接调用父类构造(不推荐)

如果不想修改父类的构造接口,可以在Amfibii中直接调用父类构造,但需要手动避免重复初始化Vehicle:

class Amfibii(Car, Boat):
    def __init__(self, name: str, n_wheels: int, n_props: int) -> None:
        # 直接调用Vehicle的构造,避免重复执行
        Vehicle.__init__(self, name)
        # 单独初始化Car和Boat的特有属性
        self.wheels = n_wheels
        print(f'Creating a Car: {name}')
        self.propellers = n_props
        print(f'Creating a Boat: {name}')
        print(f'Creating an Amfibii: {name}')

这种方法的缺点是破坏了继承的封装性,且无法适配更复杂的多继承场景,仅适用于简单场景。

内容的提问来源于stack exchange,提问作者Karol Borkowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 14:22:25