# Python多继承的MRO是什么?super()如何工作?
面试官问:"Python的MRO是什么?为什么要用super()而不是直接调用父类?"
很多人知道MRO决定了方法查找的顺序,但说不清楚为什么。真正要讲清楚的是:MRO(Method Resolution Order)用C3线性化算法保证每个类只出现一次、保持继承顺序,解决了钻石继承问题。 super()不是找父类,而是按MRO找下一个类,在多继承时能避免重复调用和顺序错误。
# 简要回答
MRO是Python查找方法和属性的顺序。 对每个类,Python维护一个__mro__元组,列出所有要搜索的类。通过__mro__属性或mro()方法可以查看。
super()按MRO找下一个类,而不是简单地找父类。 在单继承时,super().method()相当于调用父类的方法;在多继承时,它会按MRO找下一个有该方法的类。这样能避免重复调用和顺序错误。
C3线性化是MRO的算法。 它保证:继承顺序保持(父类出现在子类之后),多个父类的相对顺序保持(左到右),每个类在MRO里只出现一次。
# 详细回答
# MRO的作用与查看
在单继承中,MRO很简单:
class A:
pass
class B(A):
pass
class C(B):
pass
print(C.__mro__)
# (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
# 或者用mro()方法
print(C.mro())
# [<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
查找C的方法或属性时,Python按顺序搜索:C → B → A → object。
在多继承中,MRO必须满足特定条件:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
2
3
4
5
6
7
8
9
10
11
12
13
14
这是C3线性化的结果。D的父类是B和C,B和C都继承自A。MRO保证:
D在最前面(子类优先)。B在C之前(遵循class D(B, C)的左到右顺序)。C在A之前(子类在父类之前)。A在object之前。
# 钻石继承问题
如果不小心,多继承会导致一个方法被调用多次:
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
A.method(self) # 错误的做法:直接调用A
class C(A):
def method(self):
print("C.method")
A.method(self) # 错误的做法:直接调用A
class D(B, C):
def method(self):
print("D.method")
B.method(self)
d = D()
d.method()
# 输出:
# D.method
# B.method
# A.method
# C.method
# A.method <- A被调用了两次!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
A.method被调用了两次。使用super()可以解决:
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
super().method() # 正确的做法
class C(A):
def method(self):
print("C.method")
super().method()
class D(B, C):
def method(self):
print("D.method")
super().method()
d = D()
d.method()
# 输出:
# D.method
# B.method
# C.method
# A.method <- A只被调用一次
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
现在A.method只被调用一次。这是因为super()按MRO调用,每个类只出现一次。
# C3线性化算法
C3线性化保证MRO满足三个条件:
- 子类在父类之前。
- 多个父类的顺序遵循继承声明的左到右顺序。
- 每个类只出现一次。
简单说,MRO是一条链,按照"最近的优先、左到右"的规则排列。

# super()的工作原理
super()返回一个临时对象,这个对象的方法查找从MRO的当前位置向下开始,而不是从类的开头开始。
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method")
# super()返回一个临时对象,按MRO从B的下一个类开始查找
super().method()
class C(B):
pass
c = C()
c.method()
# C的MRO是:C -> B -> A -> object
# c.method()调用C继承的B.method()
# 在B.method()中,super().method()按MRO从B的下一个(A)开始查找
# 输出:
# B.method
# A.method
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
super()的两个形式:
class Base:
def method(self):
print("Base")
class Derived(Base):
def method(self):
# 形式1:不传参数,Python自动推导
super().method()
# 形式2:显式传参(Python 2风格,Python 3不需要)
super(Derived, self).method()
2
3
4
5
6
7
8
9
10
11
Python 3推荐用super()(无参),自动推导当前类和self。显式传参是为了兼容Python 2。
# MRO的最佳实践
1. 要么不用多继承,要么所有类都用super():
混用super()和直接调用父类名会出问题。要么全部用super()(协作式多继承),要么不用多继承。
2. 遵循继承顺序的左到右规则:
class Flyable:
def move(self):
print("Flying")
class Swimmable:
def move(self):
print("Swimming")
# 推荐:更具体的能力在前
class Duck(Flyable, Swimmable):
pass
duck = Duck()
duck.move() # Flying,因为Flyable在前
2
3
4
5
6
7
8
9
10
11
12
13
14
3. 避免深层复杂的多继承:
多继承的MRO很容易令人困惑。如果继承深度和复杂度高,考虑用组合代替继承。
# 不推荐:多层多继承
class A(X, Y):
pass
class B(A, Z):
pass
# 推荐:组合
class B:
def __init__(self):
self.a = A()
self.z = Z()
2
3
4
5
6
7
8
9
10
11
12
# 知识拓展
Q1:为什么Python 3的super()可以不传参数?
Python 3的编译器在函数体里插入了隐式的__class__单元格,存储当前类。同时,super()的第二个参数默认是self。所以super()等价于super(__class__, self)。
Q2:能修改MRO吗?
不能。MRO是由继承关系和C3线性化算法自动计算的。如果你想修改,就要改变继承结构。
如果继承结构违反了C3线性化的条件,Python会报错:
class A(B, C):
pass
class B(A): # 错误:循环继承
pass
# TypeError: Cannot create a consistent method resolution order (MRO)
2
3
4
5
6
7
Q3:super()可以跳层吗?
不能跳层。super()只能按MRO逐层调用。如果想跳过某个类,要么修改继承关系,要么直接调用特定类的方法(但不推荐)。
Q4:object类在MRO的最后,它有什么方法?
object是所有类的最终父类。它提供了基础方法如__init__、__str__、__repr__、__new__等。在多继承链的最后,super().method()会调用object的方法。
Q5:如何诊断MRO问题?
- 打印
__mro__或mro(),查看类的查找顺序。 - 在各个方法里加
print()或断点,观察调用顺序。 - 如果出现"Cannot create a consistent method resolution order"错误,说明继承结构矛盾,需要重新设计。
上一篇:Python如何实现封装、继承和多态?
下一篇:Python常见魔术方法有哪些?
评论
验证登录状态...