每天学习一点Python——super() 从困惑到精通
用最通俗的方式,彻底讲透 Python 中的 super() 和 MRO 机制
引言:为什么 super() 令人困惑?
很多 Python 初学者看到 super() 时都会有这样的疑问:
- • 明明可以用
父类名.方法(self),为什么要用 super()? - • 在多继承中,
super() 到底调用了哪个父类?
今天,我们用对话的形式,彻底解开这些疑惑。
第一部分:super() 的三大优势
1. 避免硬编码父类名
# ❌ 硬编码方式(不推荐)
class Parent:
def show(self):
return "Parent"
class Child(Parent):
def show(self):
return "Child " + Parent.show(self) # 写死了 Parent
# ✅ 使用 super()(推荐)
class ChildBetter(Parent):
def show(self):
return "Child " + super().show() # 自动找父类
优势:当修改继承关系时,super() 会自动适应,无需修改代码。
2. 支持多继承
这是 super() 最强大的功能,我们稍后会详细展开。
3. 更 Pythonic
# Python 2 风格(已过时)
super(Child, self).__init__(name)
# Python 3 风格(简洁)
super().__init__(name)
第二部分:一个令人困惑的例子
让我们从一个具体的代码开始:
class A:
def show(self):
print("A")
return "A"
class B(A):
def show(self):
print("B start")
result = super().show()
print("B end")
return "B → " + result
class C(A):
def show(self):
print("C start")
result = super().show()
print("C end")
return "C → " + result
class D(B, C):
def show(self):
print("D start")
result = super().show()
print("D end")
return "D → " + result
# 查看方法解析顺序
print(D.__mro__) # 输出: (D, B, C, A, object)
d = D()
result = d.show()
print(f"\n最终结果: {result}")
运行这段代码,你会看到:
D start
B start
C start
A
C end
B end
D end
最终结果: D → B → C → A
等一下!这里有个关键问题:
在类 B 中,super().show() 为什么调用了 C 的 show 方法?
B 不是继承自 A 吗?按理说应该调用 A 才对啊!
第三部分:理解 MRO(方法解析顺序)
什么是 MRO?
MRO(Method Resolution Order)是 Python 在多继承中决定方法调用顺序的规则。
# 继承结构
# A
# / \
# B C
# \ /
# D
print(D.__mro__) # (D, B, C, A, object)
MRO 的基本原则
关键理解:super() 不是调用父类!
这是最重要的概念:
- •
super() 是按照当前实例的 MRO,调用"下一个"类
d = D() # 创建 D 的实例
print(D.__mro__) # (D, B, C, A, object)
# 当 d.show() 时:
# 1. D.show() 中的 super() → 调用 B.show() (下一个是 B)
# 2. B.show() 中的 super() → 调用 C.show() (下一个是 C,不是 A!)
# 3. C.show() 中的 super() → 调用 A.show() (下一个是 A)
为什么在 B 中 super() 调用 C 而不是 A?
因为实例 d 是 D 类型,D 的 MRO 是 (D, B, C, A, object),所以在 B 的位置,下一个就是 C!
第四部分:详细执行流程分析
让我们一步步跟踪代码的执行:
执行栈跟踪
时间 | 当前执行位置 | 执行内容 | 输出
-----|------------------|--------------------------|-------
t1 | D.show() 第1行 | print("D start") | D start
t2 | D.show() 第2行 | super().show() → 调用 B.show()
t3 | B.show() 第1行 | print("B start") | B start
t4 | B.show() 第2行 | super().show() → 调用 C.show()
t5 | C.show() 第1行 | print("C start") | C start
t6 | C.show() 第2行 | super().show() → 调用 A.show()
t7 | A.show() 第1行 | print("A") | A
t8 | A.show() 返回 | return "A" → 返回到 C.show()
t9 | C.show() 接收结果 | result = "A"
t10 | C.show() 第3行 | print("C end") | C end
t11 | C.show() 返回 | return "C → " + "A" = "C → A"
t12 | B.show() 接收结果 | result = "C → A"
t13 | B.show() 第3行 | print("B end") | B end
t14 | B.show() 返回 | return "B → " + "C → A" = "B → C → A"
t15 | D.show() 接收结果 | result = "B → C → A"
t16 | D.show() 第3行 | print("D end") | D end
t17 | D.show() 返回 | return "D → " + "B → C → A"
可视化调用链
d.show() 开始
│
├─ print("D start")
│
├─ super().show() → B.show()
│ │
│ ├─ print("B start")
│ │
│ ├─ super().show() → C.show()
│ │ │
│ │ ├─ print("C start")
│ │ │
│ │ ├─ super().show() → A.show()
│ │ │ │
│ │ │ ├─ print("A")
│ │ │ │
│ │ │ └─ 返回 "A"
│ │ │
│ │ ├─ result = "A"
│ │ ├─ print("C end")
│ │ │
│ │ └─ 返回 "C → A"
│ │
│ ├─ result = "C → A"
│ ├─ print("B end")
│ │
│ └─ 返回 "B → C → A"
│
├─ result = "B → C → A"
├─ print("D end")
│
└─ 返回 "D → B → C → A"
第五部分:如何查看任意类的MRO
# 方法1:使用 .mro() 方法
print(D.mro())
# 输出: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
# 方法2:使用 .__mro__ 属性
print(D.__mro__)
# 输出: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
# 方法3:使用 __mro__ 并格式化
print([cls.__name__ for cls in D.__mro__])
# 输出: ['D', 'B', 'C', 'A', 'object']
为什么会在 MRO 中看到 object?
python
class A:
pass
# 等价于:
# 其实A是继承于object
class A(object): # Python 3 中这个 (object) 是隐式的
pass
print(A.__bases__) # 输出: (<class 'object'>,)
print(A.mro()) # 输出: [A, object]
第六部分:super() 的动态性演示
同一个方法,不同实例
class A:
def method(self):
print("A.method")
class B(A):
def method(self):
print("B.method start")
super().method() # 关键:super() 调用谁?
print("B.method end")
class C(A):
def method(self):
print("C.method")
class D(B, C):
pass
测试1:B的实例
b = B()
print("B的MRO:", [cls.__name__ for cls in B.__mro__])
b.method()
# 输出:
# B的MRO: ['B', 'A', 'object']
# B.method start
# A.method ← super() 调用 A
# B.method end
测试2:D的实例
d = D()
print("D的MRO:", [cls.__name__ for cls in D.__mro__])
d.method()
# 输出:
# D的MRO: ['D', 'B', 'C', 'A', 'object']
# B.method start
# C.method ← 同样的 super(),这次调用 C!
# B.method end
结论:super() 的行为取决于当前实例的 MRO,而不是固定的父类。
第七部分:常见问题解答
Q1:什么时候该用 super()?
A:在需要调用父类方法的任何地方,都应该使用 super(),特别是:
Q2:super() 一定要在方法的第一行调用吗?
A:不一定。但通常建议在 __init__ 中先调用 super().__init__(),以确保父类正确初始化。
Q3:Python 2 和 Python 3 的 super() 有什么区别?
Python 2(必须指定参数)
super(Child, self).__init__(name)
Python 3(可以省略参数)
super().__init__(name)
Q4:如果 MRO 中找不到方法怎么办?
A:super() 会一直沿着 MRO 链查找,直到找到方法或抛出 AttributeError。
第八部分:最佳实践
- 1. 始终使用 super() 而不是硬编码父类名
- 2. 保持继承链的一致性,所有类都使用 super()
- 3. 在混入类中检查父类方法是否存在:
if hasattr(super(), 'method_name'):
super().method_name()
总结
- 1.
super() 不是调用父类,而是按照当前实例的 MRO 调用下一个类 - 2.
super() 是动态的,相同代码在不同实例中可能调用不同的类 - 3. 多继承中,
super() 确保了方法调用的确定性和顺序性
理解 super() 和 MRO 是掌握 Python 面向对象编程的关键。它们让 Python 的多继承变得强大而优雅,但也需要仔细设计和理解。
希望这篇文章能帮助你彻底理解 Python 中的 super() 机制!如果有更多问题,欢迎在评论区讨论。
📦 资源获取提示
关注「码农自习室」,后台回复关键词 Python学习,即可获取本文完整代码,一起动手掌握高效编程的核心技巧!
❤️ 支持我们
如果觉得本文对你有帮助,欢迎点赞 + 关注,您的支持是我们持续创作优质内容的最大动力!
📚 学习资源说明
本文内容是基于《Python Basics: A Practical Introduction to Python 3》(Real Python)一书的学习笔记整理。
这本书是一本非常优秀的Python入门教材,推荐给所有想要系统学习Python的朋友们。
这本书的特点:
跟着这本书学习,配合我的笔记整理,相信你能更快掌握Python编程!
让我们一起坚持学习,每天进步一点点!💪