依赖注入首选构造函数注入,因其使依赖明确不可变、职责清晰、类型提示友好、测试替换成本低,并避免属性注入的运行时错误;但纯数据容器、工具函数等无需注入。

因为依赖注入能直接解决测试难、换实现难、复用难这三个最常踩的坑,而不是等项目大了再重构。
构造函数注入为什么是首选
它让依赖关系在实例化那一刻就明确、不可变,避免运行时突然缺失或被意外覆盖。
- 类的职责更清晰:
__init__参数即契约,谁用谁传,不隐藏创建逻辑 - 类型提示友好:配合
typing.Protocol或抽象基类,IDE 和 mypy 能静态检查依赖接口是否匹配 - 测试时替换成本最低:直接传入
Mock()或内存实现,不用 patch 或改全局状态 - 避免属性注入的陷阱:
self.db = None后忘记赋值,运行时报AttributeError而不是启动时报错
不手动 new 依赖的真实代价
看似少写一行传参,实则把耦合埋进类内部,后续每处修改都得同步检查所有调用点。
- 数据库从 MySQL 换成 SQLite?得搜遍所有
MySQLConnection()实例化位置 - 加个连接池配置?得改每个类的
__init__,还可能漏掉某个子类 - 写单元测试时 mock 数据库?必须用
@patch('module.MySQLConnection'),路径写错就静默失效 - 同一个服务在不同环境用不同日志器?只能靠 if-else 或配置开关,逻辑混杂
什么时候不该用依赖注入
不是所有对象都值得注入。过度使用会让简单逻辑变得臃肿,反而降低可读性。
- 纯数据容器(如
NamedTuple、dataclass)不需要注入,它们本就不含行为 - 工具函数(如
format_date()、slugify())无状态、无外部依赖,直接调用即可 - 临时对象(如循环里新建的
dict、list)生命周期短,注入反而增加调用方负担 - 真正需要控制生命周期的才注入:数据库连接、HTTP 客户端、缓存实例、通知服务等
最容易被忽略的是:依赖注入本身不解决“怎么创建依赖”的问题,只解决“谁来创建、谁来传递”。你仍需决定是在应用启动时集中初始化,还是按需懒加载——这个决策比“要不要注入”影响更大。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











