3、依赖管理:requirements.txt的正确写法,如何锁定版本,避免“在我电脑上能跑”的尴尬。
说实话,做跨平台打包最怕听到的一句话就是——“在我电脑上能跑啊!”
我遇到过太多次了。本地开发环境一切正常,代码一提交,同事拉下来跑不起来。或者更惨,打包发给客户,直接报错。问题出在哪?十有八九是依赖管理没做好。
今天我们就聊聊 requirements.txt 的正确写法。这东西看着简单,但坑不少。
为什么不能直接 pip freeze > requirements.txt?
很多新手喜欢这么干:
pip freeze > requirements.txt
嗯,这确实能生成一个依赖列表。但问题来了——它会把你当前环境里所有的包都列进去,包括那些间接依赖。
举个例子,你项目里只用了 requests,但 pip freeze 可能会输出几十个包。因为 requests 依赖了 urllib3、certifi 等等。
这样做有什么坏处?
- 冗余太多:别人看你的 requirements.txt,根本分不清哪些是直接依赖。
- 版本冲突:间接依赖的版本可能被其他包锁定,导致安装失败。
- 维护困难:升级一个包,你得手动去改一堆版本号。
pip freeze 的输出直接当 requirements.txt 用。尤其是跨平台打包时,不同系统的底层依赖不一样,这么搞必出问题。
正确写法:只列直接依赖
我个人习惯的做法是——手动维护 requirements.txt,只写项目直接依赖的包。
比如这样:
requests==2.31.0
flask==3.0.0
numpy==1.26.0
pandas==2.1.0
简洁明了。别人一看就知道项目用了哪些库。
那间接依赖怎么办?交给 pip 自己去解析。你想想看,pip 本来就会自动安装依赖的依赖,你何必替它操心?
锁定版本:用 == 还是 >= ?
这里有个常见的争论。有人喜欢用 >=,觉得这样能自动获取新版本。但我建议——打包项目一定要用 == 锁定版本。
为什么?
>=会导致不同时间安装的版本不一样。今天装的是 1.0,明天可能就是 1.1 了。- 新版本可能引入不兼容的改动。我遇到过
numpy升级后,某个函数被废弃了,代码直接报错。 - 跨平台打包时,版本不一致会导致行为差异。你在 Windows 上测试通过,Linux 上可能就挂了。
>= 灵活升级,生产环境和打包环境用 == 锁定版本。
实战:如何生成干净的 requirements.txt?
如果你已经用 pip freeze 生成了一个乱七八糟的列表,别慌。我教你一个办法。
首先,创建一个虚拟环境:
python -m venv venv
source venv/bin/activate # Windows 用 venv\Scripts\activate
然后,只安装你需要的包:
pip install requests flask numpy pandas
最后,再执行 pip freeze:
pip freeze > requirements.txt
这时候生成的列表就干净多了。但还是会包含间接依赖。怎么办?
我个人习惯用 pipreqs 这个工具。它能自动分析你的代码,只生成直接依赖:
pip install pipreqs
pipreqs ./ --force
这样生成的 requirements.txt 就非常干净了。
pipreqs。它会自动扫描项目目录,省去手动输入的麻烦。
跨平台打包的特殊注意事项
跨平台打包时,依赖管理会更复杂。因为不同操作系统可能有不同的底层依赖。
比如 pywin32 这个包,只在 Windows 上有用。如果你在 Linux 上打包,就不应该包含它。
我建议的做法是——维护多个 requirements 文件:
| 文件名 | 用途 |
|---|---|
| requirements.txt | 通用依赖,所有平台都需要 |
| requirements-win.txt | Windows 专属依赖 |
| requirements-linux.txt | Linux 专属依赖 |
| requirements-mac.txt | macOS 专属依赖 |
然后在打包脚本里,根据目标平台选择对应的文件:
# 伪代码示例
if platform == "win32":
install_requirements("requirements-win.txt")
elif platform == "linux":
install_requirements("requirements-linux.txt")
else:
install_requirements("requirements-mac.txt")
# 最后安装通用依赖
install_requirements("requirements.txt")
这样做的好处是——每个平台的依赖都清晰可控,不会出现“Windows 上能跑,Linux 上跑不了”的情况。
避坑指南:我曾经踩过的坑
我曾经犯过一个低级错误——在 requirements.txt 里写了 numpy 但没有指定版本。结果打包时,pip 自动安装了最新版。而最新版要求 Python 3.10 以上,我的打包环境是 Python 3.8,直接报错。
从那以后,我养成了一个习惯:每次更新依赖后,都在干净的虚拟环境里测试一遍。确保所有版本都能正常安装和运行。
还有一个坑——torch 这种包,不同平台有不同的 wheel 文件。如果你在 requirements.txt 里直接写 torch==2.0.0,pip 可能会下载错误的版本。
我的解决方案是——用 --find-links 指定下载源,或者手动下载对应平台的 wheel 文件。
总结一下
依赖管理看似简单,但做不好就是灾难。记住几个要点:
- 只列直接依赖,别用
pip freeze的原始输出。 - 用
==锁定版本,别用>=给自己挖坑。 - 跨平台打包时,维护多个 requirements 文件。
- 每次更新依赖后,在干净环境里测试一遍。
做到这些,“在我电脑上能跑”这种话,基本就不会从你嘴里说出来了。