继承是面向对象的核心机制之一,但 Python 的继承比 Java 或 C++ 更灵活,也更复杂。它支持多重继承,并且有一套名为 C3 线性化的算法来决定方法调用顺序(MRO)。
super() 函数的行为也和直觉不太一样 —— 它并不总是调用"父类",而是按照 MRO 顺序调用"下一个类"。理解这三者的关系,是写好 Python 类层次结构的关键。
本章从基础单继承讲起,逐步深入到多重继承、MRO 和 super() 的协作机制。
单继承指一个子类只继承一个父类,语法:
class 父类:
pass
class 子类(父类):
pass子类自动拥有父类全部属性、普通方法,复用父类代码,是 Python 最基础的继承形式。
简单示例:
class Animal:
"""动物类的基类"""
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return f"{self.name} makes a sound"
def move(self) -> str:
return f"{self.name} is moving"
class Dog(Animal):
"""狗类,继承自动物类"""
def __init__(self, name: str, breed: str):
super().__init__(name) # 调用父类构造
self.breed = breed
def speak(self) -> str:
return f"{self.name} says: Woof!"
# 使用
d = Dog("Buddy", "Golden Retriever")
print(d.speak()) # Buddy says: Woof!
print(d.move()) # Buddy is moving —— 继承自 Animal上面代码,Dog 继承了 Animal 的 move 方法,但重写了 speak 方法。super().__init__(name) 调用了父类的构造函数,避免重复初始化 name 字段。这是 super() 在单继承中最常见的用法,看起来简单,但它已经为后面的复杂性埋下了伏笔。
当你访问 d.speak() 时,Python 的查找顺序是:
实例 d 的 __dict__;
类 Dog 的 __dict__;
按照 MRO 顺序继续查找父类。
如果没有找到,抛出 AttributeError。这个查找顺序不是简单的"父类→祖类",而是 MRO 决定的线性序列。
Python 从设计之初就支持多重继承:一个类可以同时继承多个父类。语法:
class 父类1:
pass
class 父类2:
pass
class 子类(父类1, 父类2):
pass简单示例:
class Flyer:
"""飞行类"""
def fly(self) -> str:
return f"{self.name} is flying"
class Swimmer:
"""游泳类"""
def swim(self) -> str:
return f"{self.name} is swimming"
class Duck(Flyer, Swimmer):
"""鸭子类,继承了飞行类和游泳类"""
def __init__(self, name: str):
self.name = name
d = Duck("Donald")
print(d.fly()) # Donald is flying
print(d.swim()) # Donald is swimming多重继承的好处是代码复用极其灵活:Duck 同时获得了飞行和游泳的能力。但问题也来了:如果 Flyer 和 Swimmer 都定义了同名的方法(比如 move()),Python 该调用哪个?
什么是菱形继承,简单来说就是继承结构像菱形:
父类A
/ \
B类 C类
\ /
D子类上图中,D 同时继承 B、C,B 和 C 又都继承了同一个父类 A,形成菱形结构。
如果 A 里有构造方法、成员属性、同名方法,不加特殊处理时,父类 A 会被重复初始化、方法重复执行。这就是多重继承中最经典的陷阱。
简单示例:
class A:
def greet(self):
print("A says hello")
class B(A):
def greet(self):
print("B says hello")
super().greet() # 注意:使用的是 super()
class C(A):
def greet(self):
print("C says hello")
super().greet()
class D(B, C):
pass
d = D()
d.greet()运行代码,输出:
B says hello
C says hello
A says hello上面代码分别先调用 B 类的 greet()、C 类的 greet() 和 A 类的 greet(),后续介绍为什么会这样调用。
Python3 使用 C3 线性化算法(C3 Linearization)计算 MRO。每个类都有一个 __mro__ 属性,展示方法查找的线性顺序:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# 输出:(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# 等价方法
print(D.mro())
# 输出:[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]MRO 的排列规则:
子类永远在父类前面;
如果有多个父类,按继承声明的顺序从左到右;
共同祖先(如 A)出现在所有子类之后。
对于 D(B, C),MRO 是 D → B → C → A → object。当调用 d.greet() 时,Python 沿着这个顺序找第一个匹配的 greet() 方法。
C3 线性化算法看起来很玄,但可以通过一个简单的直觉理解:
D(B, C) 的 MRO = D + merge(MRO(B), MRO(C), [B, C])即:把子类自己放在最前面,然后递归合并父类的 MRO 列表。合并时保持父类内部的相对顺序,同时保证子类在父类前面。如果无法构造一个满足所有约束的线性序列,Python 会抛出 TypeError: Cannot create a consistent method resolution order。
# 这个会报错
class X(A, B): pass # 如果 A 和 B 的顺序违反约束下面通过例子展示私有 MRO 和不使用 MRO 的区别:
class Base:
def __init__(self):
print("Base init")
self.base_flag = True
class Mixin1(Base):
def __init__(self):
print("Mixin1 init")
super().__init__() # 使用 MRO
self.mixin1_flag = True
class Mixin2(Base):
def __init__(self):
print("Mixin2 init")
super().__init__() # 使用 MRO
self.mixin2_flag = True
class Combined(Mixin1, Mixin2):
def __init__(self):
print("Combined init")
super().__init__() # 使用 MRO
self.combined_flag = True
print("MRO:", [c.__name__ for c in Combined.__mro__])
c = Combined()
print("Flags:", hasattr(c, 'base_flag'), hasattr(c, 'mixin1_flag'), hasattr(c, 'mixin2_flag'))运行代码,输出:
MRO: ['Combined', 'Mixin1', 'Mixin2', 'Base', 'object']
Combined init
Mixin1 init
Mixin2 init
Base init
Flags: True True True代码 super().__init__() 中的 super() 不绑定固定类,依托当前实例的 MRO 线性继承列表动态取下一个类,是动态顺着 MRO 串行调用,天然适配多重继承、菱形继承。
注意:所有 __init__ 都被调用了一次,包括 Base!这就是 super() 配合 MRO 的威力。如果 Mixin1 直接写 Base.__init__(self),结果就会完全不同。例如:
class Base:
def __init__(self):
print("Base init")
self.base_flag = True
class Mixin1(Base):
def __init__(self):
print("Mixin1 init")
Base.__init__(self) # 不使用 MRO,直接调用 Base 类的初始方法
self.mixin1_flag = True
class Mixin2(Base):
def __init__(self):
print("Mixin2 init")
Base.__init__(self) # 不使用 MRO
self.mixin2_flag = True
class Combined(Mixin1, Mixin2):
def __init__(self):
print("Combined init")
super().__init__()
self.combined_flag = True
print("MRO:", [c.__name__ for c in Combined.__mro__])
c = Combined()
print("Flags:", hasattr(c, 'base_flag'), hasattr(c, 'mixin1_flag'), hasattr(c, 'mixin2_flag'))运行代码,输出:
MRO: ['Combined', 'Mixin1', 'Mixin2', 'Base', 'object']
Combined init
Mixin1 init
Base init
Flags: True True False上面显式指定类名调用(Base.__init__(self)),完全无视 MRO 继承链路,就是单纯以当前实例为入参,强行执行指定类的构造函数,是静态绑定。
执行路径:
Combined.__init__()
打印 Combined init
super().__init__() 依照 MRO 走到 Mixin1.__init__()
Mixin1.__init__()
打印 Mixin1 init
手写 Base.__init__(self):直接调用 Base 构造,打印 Base init
绑定 mixin1_flag
Mixin1 构造执行完毕,回到 Combined 的 super 调用结束,不会自动进入 Mixin2;
这是 Python 继承中最容易误解的点。super() 不是"调用父类",而是 "调用 MRO 中当前类的下一个类"。
示例代码:
class Animal:
def __init__(self, name):
self.name = name
print(f"Animal init: {name}")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 等价于 super(Dog, self).__init__(name)
self.breed = breed
print(f"Dog init: {breed}")
d = Dog("Buddy", "Golden")
# Animal init: Buddy
# Dog init: Golden在单继承中,super() 确实调用了父类,因为 MRO 中 Dog 的下一个类就是 Animal。这让人误以为 super() = "父类",但多重继承会打破这个直觉。
示例代码:
class A:
def __init__(self):
print("A")
super().__init__()
class B(A):
def __init__(self):
print("B")
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super().__init__()
print("MRO:", [c.__name__ for c in D.__mro__])
d = D()输出:
MRO: ['D', 'B', 'C', 'A', 'object']
D
B
C
Asuper() 的调用链是:

D.__init__ 里的 super() → 调用 B.__init__;
B.__init__ 里的 super() → 调用 C.__init__(不是 A!);
C.__init__ 里的 super() → 调用 A.__init__;
A.__init__ 里的 super() → 调用 object.__init__(空操作)。
注意:B 的 super() 没有调用 B 的父类 A,而是调用了 MRO 中 B 的下一个类 C。这就是 super() 的真正语义:按照 MRO 的线性序列,调用下一个类的方法。
这种设计解决了菱形问题中共同祖先被重复调用的问题。如果 B 和 C 都用 super(),那么 A 只会被调用一次,因为 B 的 super() 把控制权交给了 C,C 的 super() 再交给 A。整个调用链沿着 MRO 走一遍,每个类只执行一次。
class A:
def __init__(self):
print("A")
super().__init__()
# 对比:不用 super() 用直接调用
class B_direct(A):
def __init__(self):
print("B")
A.__init__(self) # 硬编码调用 A
class C_direct(A):
def __init__(self):
print("C")
A.__init__(self) # 硬编码调用 A
class D_direct(B_direct, C_direct):
def __init__(self):
print("D")
B_direct.__init__(self)
# 如果这里也调 C_direct.__init__(self),A 会被调用两次
print("MRO:", [c.__name__ for c in D_direct.__mro__])
d = D_direct()
# 输出:D → B → A(C 被跳过了!)
# 或者 D → B → A → C → A(A 被调两次!)调用代码,输出:
MRO: ['D_direct', 'B_direct', 'C_direct', 'A', 'object']
D
B
A不用 super() 的话,你不得不在 D 里手动决定调用 B 还是 C,或者两个都调导致 A 重复。super() + MRO 的组合让这个决策自动化了。
class Child(Parent):
def method(self):
super().method() # Python3 推荐写法Python3 的 super() 可以不带参数,编译器会自动推断出当前类和当前实例。这是语法糖,等价于 super(Child, self),但更可读、更不容易出错。
class Child(Parent):
def method(self):
super(Child, self).method() # Python 2 风格两个参数的含义:
第一个参数:type,当前类;
第二个参数:object 或 type,当前实例或子类。
super(Child, self) 会从 MRO 中 Child 的下一个类开始查找。如果你传入的是子类(比如 super(Child, GrandChild)),它会从 MRO 中 Child 的下一个类开始查找,跳过 Child 本身。这在复杂元编程中偶尔用到,但日常代码里没必要。
class Base:
def work(self, x):
return x * 2
class Child(Base):
def work(self, x):
base_result = super().work(x)
return base_result + 1
c = Child()
print(c.work(5)) # 11 (5*2 + 1)super().work(x) 调用了 MRO 中下一个类的 work 方法。在单继承中就是父类,但记住这个语义在多重继承中同样适用。
Mixin 是一种特殊的多重继承用法:用小型、单一的类提供某种功能,本身不单独实例化。配合 super(),Mixin 可以像积木一样组合。例如:
class BaseHolder:
def __init__(self, *args, **kwargs):
# 吃掉所有多余关键字参数,不再往上抛给 object
pass
class JsonSerializable:
"""Mixin:提供 JSON 序列化能力"""
def to_json(self):
import json
return json.dumps(self.__dict__)
class Timestamped:
"""Mixin:提供创建时间戳"""
def __init__(self, *args, **kwargs):
from datetime import datetime
self.created_at = datetime.now().isoformat()
super().__init__(*args, **kwargs)
class Validated:
"""Mixin:参数校验"""
def __init__(self, *args, **kwargs):
if 'name' not in kwargs or not kwargs['name']:
raise ValueError("name is required")
super().__init__(*args, **kwargs)
class User(Timestamped, Validated, JsonSerializable, BaseHolder):
"""用户类,继承多个 Mixin,组装出一个功能丰富的类"""
def __init__(self, name: str, age: int = 0):
super().__init__(name=name, age=age) # 按 MRO 链式传递 kwargs
self.name = name
self.age = age
if __name__ == "__main__":
u = User("Alice", 30)
print(u.to_json())
# 输出类似:{"name": "Alice", "age": 30, "created_at": "2024-..."}这个例子有几个关键设计点:
每个 Mixin 的 __init__ 都接受 *args, **kwargs 并传给 super(),保证参数能沿 MRO 链传递;
Timestamped 把 created_at 注入实例,不依赖具体业务类;
3. User 的继承顺序决定了初始化顺序:User → Timestamped → Validated → JsonSerializable → BaseHolder → object。
注意:Mixin 的继承顺序很重要。有依赖的 Mixin(如 Validated 需要提前检查参数)应该放在后面(靠近基类),因为 super() 是从左到右执行的。
class A:
def __init__(self):
print("A")
super().__init__()
class B(A):
def __init__(self):
print("B")
# 忘记 super()!
class C(B):
def __init__(self):
print("C")
super().__init__()
C()
# 输出:C → B(A 被跳过了!)只要链上有一个类没调 super(),MRO 链就断了。设计 Mixin 或基类时,如果预期会被组合,每个 __init__ 都必须调用 super()。
class X: pass
class Y(X): pass
class Z(X): pass
# 下面的声明违反了约束:Y 在 X 前面,但 Z 继承要求 X 在 Z 前面
class A(Y, Z): pass
# 可以,MRO 没问题
class B(Z, Y): pass
# 也可以
class C(A, B): pass # TypeError!错误信息如下:
Traceback (most recent call last):
File "d:\python_demo\demo.py", line 12, in <module>
class C(A, B): pass # TypeError!
^^^^^^^^^^^^^^^^^^^
TypeError: Cannot create a consistent method resolution order (MRO) for bases Y, ZC(A, B) 会报错,因为 A 的 MRO 要求 Y 在 Z 前面,而 B 的 MRO 要求 Z 在 Y 前面,无法构造一个同时满足两者的线性序列。遇到这种错误,说明类层次设计有冲突,需要重构继承关系。
class Base:
@classmethod
def create(cls, name):
print(f"Base.create: {cls.__name__}")
return cls(name)
class Child(Base):
def __init__(self, name):
print(f"Child.__init__: {name}")
self.name = name
@classmethod
def create(cls, name):
print(f"Child.create: {cls.__name__}")
return super().create(name)
c = Child.create("test")
print(type(c)) # <class '__main__.Child'>
print(c.name) # test运行代码,输出如下:
Child.create: Child
Base.create: Child
Child.__init__: test
<class '__main__.Child'>
testsuper() 在类方法里同样遵循 MRO,返回的绑定对象会把方法调用转发到 MRO 中的下一个类。cls 参数会被正确传递,这是工厂模式配合继承的标准写法。
class Animal: pass
class Dog(Animal): pass
d = Dog()
print(isinstance(d, Dog)) # True
print(isinstance(d, Animal)) # True
print(issubclass(Dog, Animal)) # True
print(issubclass(Dog, object)) # Trueisinstance 和 issubclass 都会考虑 MRO。isinstance(d, Animal) 为 True,是因为 Animal 在 Dog 的 MRO 中。isinstance 比 type(d) == Dog 更灵活,通常优先使用。