Python 组合优于继承的实践示例
发布时间 - 2026-01-30 00:00:00 点击率:次该用 class A 包含 B() 而不是 class A(B) 当只需部分父类行为且需灵活替换、测试隔离或避免强耦合时;继承会绑定全部接口与生命周期,易因 B 变更导致 A 失效。
什么时候该用 class A 包含 B() 而不是 class A(B)
当子类只需要部分父类行为,且这些行为可能随上下文变化、需要替换或测试隔离时,组合更合适。继承会把 B 的全部接口和生命周期强绑定到 A,一旦 B 内部改了(比如加了新方法或修改了 __init__ 参数),A 就可能意外出错。
常见信号包括:
- 你只调用
B的 1–2 个方法,却继承了它全部 20 个方法和属性 -
A的测试需要 mockB的行为——用继承时只能 patch 类方法或绕过super().__init__,而组合直接传入 mock 实例即可 -
B是第三方类(如requests.Session或sqlite3.Connection),你无法控制其演进
__init__ 里传实例比在类里硬编码 B() 更灵活
硬编码 self.db = Database() 会让单元测试难写,也堵死了换存储后端的路。应该把依赖显式传进来,哪怕默认给一个。
class UserService:
def __init__(self, db=None):
self.db = db or Database() # 可选,默认构造
这样调用时可以自由替换:
- 测试:
UserService(MockDB()) - 开发:
UserService(InMemoryDB()) - 生产:
UserService(PostgresDB(host="..."))
注意:不要在 __init__ 里做重操作(如连接数据库),否则组合对象初始化失败时难以定位是自身问题还是依赖问题。
用属性委托代替继承后的方法转发
继承后如果只转发几个方法(比如 def save(self, *a): return self.db.save(*a)),不如用 __getattr__ 动态代理——减少样板代码,还能统一处理未实现方法。
class Repository:
def __init__(self, backend):
self._backend = backend
def __getattr__(self, name):
return getattr(self._backend, name)
但要注意:
-
__getattr__不拦截已定义的属性或方法(如__init__、__str__),所以不会覆盖你自己的逻辑 - 如果
_backend某方法抛AttributeError,会被吞掉并转抛为Repository的 AttributeError,调试时容易误判来源 - IDE 和类型检查器(如 mypy)可能无法推导动态代理的方法,需配
typing.overload或Protocol
组合后别忘了生命周期管理
继承时,父类资源通常和子类共生死;组合则要自己管。比如 self.session = requests.Session(),你得确保它被关闭——否则连接泄漏。
- 用上下文管理器:
with UserService(db) as service:,在__enter__/__exit__中控制db.close() - 或者暴露显式清理方法:
service.close(),并在文档里强调“必须调用” - 避免在
__del__里关资源——不可靠,且可能触发循环引用
最易忽略的是:组合对象被长期持有(如全局单例或 Flask 应用中的 g 对象)时,底层依赖的连接/文件句柄是否复用合理、有没有超时重连逻辑。
# python
# 编码
# session
# 后端
# 动态代理
# flask
# 父类
# 子类
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
音响网站制作视频教程,隆霸音响官方网站?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
香港服务器租用费用高吗?如何避免常见误区?
制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?
iOS正则表达式验证手机号、邮箱、身份证号等
Laravel如何与Inertia.js和Vue/React构建现代单页应用
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
Laravel如何实现API版本控制_Laravel API版本化路由设计策略
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
Windows10如何更改计算机工作组_Win10系统属性修改Workgroup
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
Laravel如何实现密码重置功能_Laravel密码找回与重置流程
如何在阿里云香港服务器快速搭建网站?
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
个人摄影网站制作流程,摄影爱好者都去什么网站?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
iOS发送验证码倒计时应用
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】
网页设计与网站制作内容,怎样注册网站?
Laravel怎么创建自己的包(Package)_Laravel扩展包开发入门到发布
如何实现javascript表单验证_正则表达式有哪些实用技巧
如何快速查询网站的真实建站时间?
Python文件异常处理策略_健壮性说明【指导】
如何有效防御Web建站篡改攻击?
Win11怎么关闭专注助手 Win11关闭免打扰模式设置【操作】
如何快速建站并高效导出源代码?
如何快速搭建高效香港服务器网站?
Laravel如何实现数据库事务?(DB Facade示例)
桂林网站制作公司有哪些,桂林马拉松怎么报名?
微信小程序 input输入框控件详解及实例(多种示例)
,交易猫的商品怎么发布到网站上去?
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
轻松掌握MySQL函数中的last_insert_id()
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
如何将凡科建站内容保存为本地文件?
JS弹性运动实现方法分析
HTML5空格在Angular项目里怎么处理_Angular中空格的渲染问题【详解】
悟空识字如何进行跟读录音_悟空识字开启麦克风权限与录音
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
Laravel如何实现用户注册和登录?(Auth脚手架指南)
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
如何快速完成中国万网建站详细流程?
三星、SK海力士获美批准:可向中国出口芯片制造设备
Android 常见的图片加载框架详细介绍
JavaScript Ajax实现异步通信
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
python中快速进行多个字符替换的方法小结
Swift中switch语句区间和元组模式匹配
如何确认建站备案号应放置的具体位置?


