
本文首发于微信公众号「拙见python」,基础板块第 9 篇。适合人群:已阅读第 2 篇:Python 面向对象编程并动手敲过代码的读者。题目难度:⭐ → ⭐⭐⭐⭐⭐ 逐步提升,建议先自己写,再看答案。
做题须知
每道题标注了考察知识点和难度星级 先自己写,写不出来再看答案 答案不是唯一的,能跑通且逻辑正确就行 每道题答案下方附有思路解析
第 6 题:多继承与 MRO 分析 ⭐⭐⭐⭐
考察知识点:多继承、MRO(方法解析顺序)、super() 在多继承中的行为
阅读以下代码,先预测输出,再运行验证:
D的 MRO 是什么?d.show()调用的是哪个类的show()?d.greet()的输出是什么?为什么super().greet()会调用B的方法?
class A:def greet(self):print(”A.greet”)super().greet()def show(self):return ”A.show”class B(A):def greet(self):print(”B.greet”)def show(self):return ”B.show”class C(A):def greet(self):print(”C.greet”)super().greet()def show(self):return ”C.show”class D(B, C):def greet(self):print(”D.greet”)super().greet()
参考答案与解析:
# 1. 查看 MROprint(”D 的 MRO:”)for cls in D.__mro__:print(f” {cls.__name__}”)# D 的 MRO:# D# B# C# A# object# 2. d.show() 调用哪个类?d = D()print(f”\nd.show() = {d.show()}”)# d.show() = B.show# 原因:D 没有定义 show(),按 MRO 顺序查找,B 在 C 之前,所以调用 B.show()# 3. d.greet() 的输出print(”\nd.greet() 输出:”)d.greet()# D.greet# B.greet
输出结果:
D 的 MRO:DBCAobjectd.show() = B.showd.greet() 输出:D.greetB.greet
# 追问:如果 D 定义为 class D(C, B) 呢?classD2(C, B):passprint(”D2 的 MRO:”)for cls in D2.__mro__:print(f” {cls.__name__}”)# D2 → C → B → A → objectd2 = D2()print(f”\nd2.show() = {d2.show()}”)# d2.show() = C.show(C 在 B 之前了)print(”\nd2.greet() 输出:”)d2.greet()# C.greet# A.greet# 原因:C.greet 调用了 super().greet(),按 MRO 下一个是 A(不是 B,因为 B.greet 没 super())
思路解析:
MRO(Method Resolution Order) 使用 C3 线性化算法,规则是:从左到右、深度优先、但保证每个父类只出现一次且在子类之后。
class D(B, C) 的 MRO 是 D → B → C → A → object。B 在 C 之前,因为声明顺序是 (B, C)。
d.show() 返回 "B.show":D 没有 show(),按 MRO 找到 B 的 show()。
d.greet() 的调用链:
D.greet()打印 "D.greet",然后 super().greet()
MRO 中 D 的下一个是 B,所以调用 B.greet()
B.greet() 只打印 "B.greet",没有 super().greet(),调用链到此结束
改为 class D2(C, B) 后,MRO 变成 D2 → C → B → A → object,show() 和 greet() 的行为都会改变。
核心要点:super() 不是「调用父类」,而是「调用 MRO 中的下一个类」。在多继承中,super() 的目标取决于当前类在 MRO 中的位置。
建议的练习方式:
先自己写——不看答案,能写多少写多少
卡住了回看文章——第 2 篇对应章节有完整示例
关注「拙见python」,一起写干净的代码。
下一篇预告:Python 面向对象习题之7/10
夜雨聆风