来源:互联网 更新时间:2026-06-24 12:35
先坦白一件事:去年我写一个数据处理框架时,半夜三点还在跟一个Bug死磕。代码看着挺简单——用几个混入类(Mixin)来复用功能,类似下面这样:
class LogMixin:
def log(self, msg):
print(f"[LOG] {msg}")
super().log(msg) # 我也不知道为什么要调用super,但看教程都这么写
class SaveMixin:
def save(self):
print("保存数据")
super().save()
class DataProcessor(LogMixin, SaveMixin):
def process(self):
self.log("开始处理")
self.save()
结果一跑就崩了:AttributeError: 'super' object has no attribute 'log'。当时脑袋里就两个字——“什么玩意?”
为什么?super()不是调用父类吗?LogMixin根本没有父类,它调用个什么super?加super就报错,不加又怕漏掉什么,完全不知道该怎么做才对。
那晚,我把Python多重继承的机制翻了个底朝天。今天把这些坑和绕坑指南整理出来,希望能让你少失眠几个晚上。
先承认一个事实:多重继承本身就很复杂。这不是Python独有的问题,C++、Java(接口多重继承)都有类似的复杂度。但Python的多重继承有几个特点,让事情变得更微妙:
super()不是简单调用父类这些特点单独看都没问题,组合在一起就很容易写出"看着对、跑着错"的代码。
super()到底在调用谁?这是最常见的困惑。来看一个经典例子:
class A:
def method(self):
print("A")
super().method()
class B:
def method(self):
print("B")
class C(A, B):
def method(self):
print("C")
super().method()
c = C()
c.method()
输出是:
C A B
等等,A和B没有任何继承关系,为什么A里的super().method()会调到B的方法?这就是Python的
C.mro()的输出是:
[, , , ]
所以,super()不是"调用父类",而是
super().method()时,Python会去找当前类在MRO中的下一个类,然后调用它的同名方法。这就是为什么A的super()会调到B——因为MRO里A后面就是B。
super()super(),确保MRO链条上的所有类都实现了同名方法ClassName.__mro__查看调用顺序,调试时很有用当多个父类最终继承自同一个基类时,就形成了"钻石"形状:
class Top:
def __init__(self):
print("Top初始化")
self.data = []
class Left(Top):
def __init__(self):
print("Left初始化")
Top.__init__(self) # 手动调用父类
class Right(Top):
def __init__(self):
print("Right初始化")
Top.__init__(self) # 手动调用父类
class Bottom(Left, Right):
def __init__(self):
Left.__init__(self)
Right.__init__(self)
b = Bottom()
输出:
Left初始化 Top初始化 Right初始化 Top初始化 # Top被初始化了两次!
Top的__init__被执行了两次,self.data被重置了。这在真实项目中可能引发灾难。
super()代替手动调用父类。
class Top:
def __init__(self):
print("Top初始化")
super().__init__()
class Left(Top):
def __init__(self):
print("Left初始化")
super().__init__()
class Right(Top):
def __init__(self):
print("Right初始化")
super().__init__()
class Bottom(Left, Right):
def __init__(self):
print("Bottom初始化")
super().__init__()
b = Bottom()
输出:
Bottom初始化 Left初始化 Right初始化 Top初始化
Top只被调用了一次。这就是super()的魔力——它沿着MRO链条确保每个父类只被执行一次。
super()调用父类构造函数Parent.__init__(self)这个错误信息看着就让人绝望。来看一个会触发它的例子:
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass # 没问题,MRO: D -> B -> C -> A -> object
但如果继承顺序有冲突,就会报错:
class A: pass
class B: pass
class C(A, B): pass
class D(B, A): pass
class E(C, D): pass # TypeError!
Python无法找到一个既满足C的父类顺序(A在B前)、又满足D的父类顺序(B在A前)的线性化方案。这就是C3线性化算法无法处理的情况。这种错误在大型项目(比如SageMath的类别体系)中并不少见。
这个坑比较隐蔽。你想定义一个同时继承NamedTuple和其他类的类型:
from typing import NamedTuple
class Animal:
def eat(self):
print("吃")
class Person(NamedTuple, Animal): # TypeError!
name: str
age: int
报错:TypeError: Multiple inheritance with NamedTuple is not supported。因为NamedTuple是CPython用C实现的,它不支持多重继承的元类组合。
NamedTuple时,不要把它和其他类混在一起继承dataclass代替NamedTuple,dataclass对多重继承的支持更好这不是语法错误,但比语法错误更可怕。
看看Tkinter(Python的标准GUI库)的例子。tkinter.Button的实例有
import tkinter as tk
btn = tk.Button()
print(len(dir(btn))) # 214
这意味着:
Mixin后缀命名这些类,明确告诉读者"这不是完整的类,只是功能片段"如果你确实要用多重继承,
Mixin的核心思想是:
class LogMixin:
"""只提供日志功能,不定义新类型"""
def log(self, msg):
print(f"[{self.__class__.__name__}] {msg}")
# 注意:这里没有调用super()
class SaveMixin:
"""只提供保存功能"""
def save(self):
print(f"保存数据到{self.get_save_path()}")
class DataProcessor(LogMixin, SaveMixin):
def get_save_path(self):
return "/tmp/data.json"
def process(self):
self.log("开始处理")
self.save()
这样写有几个好处:
super(),避免了"调用谁"的困惑Mixin结尾,一眼就能看出它是混入类而非实体类。
说实话,
| 场景 | 是否推荐 | 理由 |
|---|---|---|
| 组合多个Mixin功能 | 推荐 | 代码复用,职责单一 |
| 需要同时具备两种"类型" | 谨慎 | 考虑是否用接口代替 |
| 为了复用代码而继承多个类 | 避免 | 用组合+委托更清晰 |
| 解决复杂的设计问题 | 避免 | 大概率是设计出了问题 |
Python的经典指导原则是:
如果确实需要多重继承,记住这三个原则:
super()统一调用
多重继承的生存指南
├── 必须用super()——永远不要手动调用父类
├── 查看MRO——用ClassName.__mro__调试
├── Mixin模式——小功能、不调用super、命名带Mixin后缀
├── 避免钻石继承——如果避免不了,让顶层类的__init__可以重复调用
└── 当不确定时——用组合代替继承
我的LogMixin里调用了super().log(msg),但LogMixin在MRO里是第一个,它后面是SaveMixin,而SaveMixin没有log方法,所以报错。
class LogMixin:
def log(self, msg):
print(f"[LOG] {msg}")
# 把super()去掉!Mixin只做自己的事
class SaveMixin:
def save(self):
print("保存数据")
# 同样不调用super()
或者,如果确实需要super()串联调用,确保链条上所有类都有同名方法。但在Mixin场景下,不调用super()是更安全的选择。
那个晚上之后,对Python多重继承算是彻底搞明白了。希望这篇文章能让你少加一次班。
《Off Campus》第二季官宣:这对CP还在,但不再是主角
币安Binance虚拟货币交易平台 币安官方APP安卓苹果下载入口
客单价碾压宝马奥迪!极氪5月交付新车34377辆:连续4个月双增长
HBO 奇幻剧《龙之家族》第三季定档 6 月 22 日,最终预告片曝光喉道海战
archiveofourown 实战指南:常见用法整理
折后价近千元 澳洲一店主将真老鼠缝到内裤上当时尚单品卖
帅气继父网名女生可爱英文(精选100个)
帅到极致的网名女生霸气(精选100个)
如何在夸克浏览器中开启网页视频的倍速播放功能?
作家助手如何上传自制封面 作家助手如何设置小说的封面
DOTA2 TI时隔七年重返上海!门票6月10日开抢,国服享受优先购买!
韩漫小少爷网名大全女生(精选100个)
网络热词聊污是什么意思
电视剧《小欢喜》剧情介绍
有寓意的易经网名男生(精选100个)
欧易OKX官方网站直达入口 2026欧易官方App安卓版v7.1.0下载安装
小众游戏抖音网名男生(精选100个)
电影《遁甲门之消失的公主》剧情介绍
美国市场:股票相对债券的风险溢价正在消失
全链网:黄金价格因美元的走强及利率担忧而下跌
手机号码测吉凶
本站所有软件,都由网友上传,如有侵犯你的版权,请发邮件haolingcc@hotmail.com 联系删除。 版权所有 Copyright@2012-2013 haoling.cc