卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

  • 面向对象

    • Python中的类和对象是什么?
    • 实例属性和类属性有什么区别?
    • 实例方法、类方法和静态方法有什么区别?
    • __new__和__init__有什么区别?
    • Python如何实现封装、继承和多态?
    • Python多继承的MRO是什么?super()如何工作?
      • 简要回答
      • 详细回答
      • 知识拓展
    • Python常见魔术方法有哪些?
  • 进阶语法与语言机制

# 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'>]
1
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'>)
1
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被调用了两次!
1
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只被调用一次
1
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满足三个条件:

  1. 子类在父类之前。
  2. 多个父类的顺序遵循继承声明的左到右顺序。
  3. 每个类只出现一次。

简单说,MRO是一条链,按照"最近的优先、左到右"的规则排列。

MRO的C3线性化和super()调用链

# 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
1
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()
1
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在前
1
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()
1
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)
1
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常见魔术方法有哪些?

Last Updated: 10/13/2026, 5:57:00 PM

← Python如何实现封装、继承和多态? Python常见魔术方法有哪些? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪