真正能复用的代码需满足输入明确、副作用可控、不硬编码路径或配置;避免顶层执行逻辑,配置通过参数传入;import路径须规范,目录含__init__.py;函数应解耦i/o与状态;测试覆盖核心场景,类型检查保障兼容性。

模块化不是把代码塞进 .py 文件就完事
很多人以为“写成函数+存成文件”就是复用,结果发现改一个地方,三个项目全崩——根本原因是没理清作用域和依赖边界。
真正能复用的代码,必须满足:输入明确、副作用可控、不硬编码路径或配置。
- 避免在模块顶层执行逻辑(比如直接调用
main()或读取os.environ),否则导入时就触发行为 - 配置项尽量通过参数传入,而不是依赖
config.py或全局变量;若必须用配置,用from pathlib import Path定位,别写死"./conf.yaml" - 第三方包版本要锁在
requirements.txt里,尤其是requests、pydantic这类接口易变的库
import 路径错乱是 80% 复用失败的根源
不是代码不能跑,而是同一段代码,在 A 项目里 import utils 没问题,挪到 B 项目就报 ModuleNotFoundError——本质是 Python 的 sys.path 和包结构不匹配。
- 永远用相对导入(
from . import helpers)或绝对导入(from myproject.utils import clean_data),禁用sys.path.append("..") - 确保每个目录含
__init__.py(哪怕空文件),否则 Python 不认它是包 - 开发时用
-m方式运行(如python -m myproject.cli),而不是python cli.py,避免当前目录干扰模块解析
函数设计要防“隐式耦合”,尤其涉及 I/O 和状态
一个叫 load_config() 的函数,表面看很通用,实际可能暗藏对本地文件系统、特定环境变量、甚至某个全局 logger 实例的依赖——这种函数在测试或跨项目使用时必然翻车。
立即学习“Python免费学习笔记(深入)”;
- 把 I/O 操作抽成参数:比如
load_config(file_path: Path),而不是内部写死Path("config.json") - 避免函数内部修改全局状态(如往
logging.getLogger()加 handler),应由调用方决定日志行为 - 返回值优先用原生类型(
dict、list、dataclass),少用自定义类,除非该类本身已作为公共契约稳定存在
测试不是可选项,而是复用的准入门槛
没测过的复用代码,等于没写。不是指覆盖率要 100%,而是至少覆盖:正常路径、空输入、异常输入、典型错误场景。
- 用
pytest写测试时,给测试函数加tmp_pathfixture 处理临时文件,别在/tmp硬写路径 - 对网络请求等外部依赖,用
responses或httpx.MockTransport拦截,别让测试依赖真实服务 - 每次发布新版本前,用
mypy检查类型注解是否一致,特别是函数签名变更后容易漏掉调用方的适配
真正难的从来不是“怎么写可复用的代码”,而是“怎么让别人不用看源码就能猜出它怎么用、在哪用、改了会牵连谁”。接口越薄,约束越显,复用才越稳。










