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 就非常干净了。

小技巧:如果你用 PyCharm,可以直接在 Terminal 里运行 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 文件。
  • 每次更新依赖后,在干净环境里测试一遍。

做到这些,“在我电脑上能跑”这种话,基本就不会从你嘴里说出来了。