组合优于继承。由于继承容易引起隐性债务、MRO混乱和参数不兼容,组合通过显式委托实现高内聚、低耦合、易测试和动态替代。
Python里class A(B)不是万能钥匙
在Python中继承确实可以快速复用代码,但一旦进入生产环境,class A(B)它很容易成为“隐性债务”。它强行将子类和父类的实现细节绑在一起——例如,父类的重构已经改变了<strong>init</strong>签名时,所有子类都必须跟随;或者父亲加了一个super().setup()调用,而某个子类忘记了自己setup()里补上super(),运行时直接抛掷AttributeError。
MRO更麻烦(Method Resolution Order)问题。当多个父类有同名方法或菱形继承时(如C(A, B)且A、B都继承D),Python通过C3算法计算调用顺序,但这个顺序不是直觉能猜到的。C.mro()输出可能令人困惑,在线错误的调查成本远高于写几行委托代码。
常见错误现象包括:
- TypeError: __init__() takes 2 positional arguments but 3 were given(父类__init__参数变更未同步)
- 意外跳过方法(MRO中的顶级类涵盖了本该执行的逻辑)
- 单元测试通过后,集成后行为异常(因为继承链中的某一层悄然修改状态)
self.engine = Engine()代替class Car(Engine)
组合的核心动作是一句话:self.xxx = SomeClass(),然后在方法中进行显式委托调用。它自然支持“运行时更换”FakeEngine(),上线后换回RealEngine(),完全不碰主类结构。
立即学习“Python免费学习笔记(深入);
这种写法使依赖关系一目了然:Car类头可以看到它“拥有”什么,而不是翻三页代码来找出它“是”什么。也避免了单继承限制——Python只允许一个class语句指定父类,但可以同时持有self.logger、self.validator、self.cache三个不同职责的对象。
Python 3.14.2
Python 3.14.2是Python编程语言于2025年12月5日发布的稳定版本,属于3.14系列的第二次维护更新。该版本包含18个修复项目,重点解决多过程、数据和正则表达模块的回归问题,修复CVE-2025-12084等安全漏洞。这个版本标志着Python发展的一个重要里程碑,即自由线程模式(删除GIL)正式得到官方支持。
下载例如:使用场景: - 需要动态切换策略(如支付方式:PayPal vs Stripe) - 多个模块需要独立进化(不影响核心业务逻辑的日志组件升级) - 第三方库不能修改,但你想重用它的能力(包装而不是继承)
super()不是银弹,self.delegate.method()是可控开关
super()看似优雅,实则把控制权交给了Python的MRO机制。它在多个继承或框架钩中(如Django) Modelsave())特别危险-你不能100%确认super().save()谁调整了,什么时候调整了,关键副作用是否执行了。
确定组合下的调用:self.db.save()就是self.db实例的save(),不会拐弯抹角。如果db对象本身也使用组合,整个链路可读、可测、可mock。
注意参数差异:
- 继承:子类__init__父亲的签名必须兼容,否则,super().__init__()会炸
- 组合:构造函数参数自由设计,__init__(self, db: DB, logger: Logger)合同表达清楚
Python支持更多的继承,但生产代码中几乎没有人真正使用它。它放大了MRO不可预测性、方法冲突、初始化顺序混乱等问题。即使只是混合一个Mixin,也可能因为super()链条断裂导致资源没有释放,钩子没有触发。
组合自然避免了这个问题:想添加功能吗?添加一个字段+几行委托。想删除功能吗?删除字段+删除相应的呼叫。没有“父亲应该重写吗?”<strong>del</strong>”、“Mixin这种烧脑题应该放在MRO的第一位。
容易踩的坑:
- 将组合写成“假继承”:def save(self): return self.db.save()写了一百遍,却没有抽成通用委托基类
- 忘记生命周期管理:组合对象需要手动操作close()或shutdown(),继承常默认的GC接管
- 过度解耦:每一个小操作都被拆分成独立的类别,反而增加了调用栈的深度和理解成本
真正困难的不是选择组合或继承,而是判断哪里应该划清界限,哪些状态应该包装成独立的对象。这不能通过语法来解决,而是通过对业务稳定性的预测——生产环境中最缺乏的是这种预测的容错空间。