典型表现为TypeError: super(type, obj): obj必须是type或其子类实例,根本原因是super(cls, obj)中obj类型与cls不匹配,多因手动传参错误或混用显式类名与super();应优先使用无参super(),并验证MRO顺序及各层__init__是否统一透传**kwargs。

super()在多继承中报错:MRO不匹配的典型表现
当调用 super().__init__() 时抛出 TypeError: super(type, obj): obj must be an instance or subtype of type,基本可以断定是子类实例与 super() 第一个参数类型不一致——常见于手动传参错误或混用显式类名与 super()。
根本原因是 Python 的 MRO(Method Resolution Order)是静态确定的,但 super(cls, obj) 要求 obj 必须是 cls 或其子类的实例。多继承下若某层构造函数误写成 super(ConcreteClass, self).__init__(),而当前 self 实际来自更深层子类,就极易触发该异常。
- 永远优先用无参
super()(Python 3+ 默认行为),它自动推导当前类和self,与 MRO 严格对齐 - 避免显式写
super(ClassName, self),除非你完全清楚当前栈帧的类、self类型及 MRO 位置 - 检查所有父类
__init__是否都接受并转发**kwargs(尤其混用旧式 mixin 时),否则参数传递中途中断会导致某层super()根本没被调用,MRO 链断裂
确认MRO顺序:别靠猜,用ClassName.__mro__看真实路径
多继承的执行顺序不等于继承声明顺序,而是 C3 线性化结果。不验证就假设 super() 会按你写的顺序调用,是多数问题的源头。
例如定义 class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
,你以为 D.__mro__ 是 (D, B, C, A, object)?错——实际是 (D, B, C, A, object)(这里碰巧一致),但换成 class E(C, B) 就变成 (E, C, B, A, object)。
- 在任意类定义后立刻打印
D.__mro__,把元组转为可读列表:list(D.__mro__) - 确保每个父类的
__init__最终都调用super().__init__(...),且参数签名兼容(推荐统一用*args, **kwargs) - 如果某个父类不调用
super()(比如直接写A.__init__(self)),则 MRO 链在此截断,后续类不会被初始化
混合使用super()和显式类名调用:危险的混搭模式
在同一个类的 __init__ 中,一半用 super(),一半写 ParentClass.__init__(self),几乎必然导致重复初始化或跳过初始化。
显式调用绕过了 MRO,而 super() 依赖 MRO;两者逻辑冲突。例如 B 类中写 A.__init__(self),那么无论 D 的 MRO 如何,A 都会在 B 这一层被硬调一次——若 C 也调了 A.__init__(self),A.__init__ 就被执行两次。
- 全项目统一风格:要么全部用
super()(推荐),要么全部用显式类名(仅限单继承且无扩展需求) - 第三方库类若未遵循
super()规范,不要直接继承并混用;考虑组合(composition)替代继承 - 调试时在每个
__init__开头加print(f"Entering {self.__class__.__name__}"),对照__mro__输出确认执行流
带参数的super()调用:为什么**kwargs比固定参数更安全
多继承中各父类对参数需求不同,硬编码 super().__init__(arg1, arg2) 很容易因某父类不接受该签名而崩溃,或漏传关键参数。
更鲁棒的做法是让每层只消费自己需要的参数,其余透传。这样即使新增一个 mixin 类,只要它也遵守透传规则,就不需要修改已有类。
- 父类
__init__声明为def __init__(self, *, required_arg=None, **kwargs):,用关键字参数隔离职责 - 末尾必须调用
super().__init__(**kwargs),保证剩余参数继续向下传递 - 最底层基类(通常是
object)不接受任何参数,所以确保参数在到达前已被全部消费,否则会报TypeError: object.__init__() takes exactly one argument
复杂点在于:MRO 是确定的,但参数消费顺序必须与 MRO 严格一致;稍有错位,就会出现参数残留或缺失。这没法靠直觉判断,只能靠 __mro__ + 打印 + 逐层验证。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











