4、依赖管理:requirements.txt编写、pip镜像源配置、依赖冲突解决

依赖管理这事儿,说大不大,说小不小。我见过太多团队,代码写得漂漂亮亮,一部署就炸——全是依赖版本打架。你想想看,一个套利系统里,pandas、numpy、ccxt、redis、sqlalchemy……十几个包挤在一起,版本稍微不对付,整个系统就罢工。

今天咱们就把这块彻底捋清楚。

4.1 requirements.txt 的正确写法

很多人写 requirements.txt 就是 pip freeze 一把梭。我个人习惯是,绝不直接用 pip freeze 的输出当生产环境的依赖文件。为什么?因为 freeze 会把所有间接依赖也打出来,版本锁得太死,换个环境就容易出问题。

我建议你手动维护一个精简版:

# 核心依赖
pandas>=1.3.0,<2.0.0
numpy>=1.21.0
ccxt>=4.0.0
redis>=4.5.0
sqlalchemy>=1.4.0,<2.0.0
apscheduler>=3.9.0

# 工具类
loguru>=0.6.0
python-dotenv>=0.20.0

# 可选依赖(按需安装)
# ta-lib>=0.4.0
# plotly>=5.10.0
我的习惯:主依赖写版本范围,不锁死小版本。这样既能保证兼容性,又能让 pip 有灵活度去解决冲突。

还有一种场景——你开发环境和生产环境依赖不一样。比如开发需要 pytest,生产不需要。这时候我推荐用多文件管理:

requirements/
├── base.txt       # 公共依赖
├── dev.txt        # 开发环境
└── prod.txt       # 生产环境

base.txt 里写核心依赖,dev.txt 和 prod.txt 用 -r base.txt 引入公共部分,再加各自的专属包。

4.2 pip 镜像源配置

在国内做部署,镜像源是绕不开的。默认的 pypi.org 慢得让人抓狂。我记得有一次在客户服务器上部署,一个 pip install 跑了半小时还没完,后来发现是网络问题。

配置镜像源,我推荐三种方式:

方式一:全局配置(一劳永逸)

# Linux/Mac
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

# Windows
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

方式二:临时指定(一次有效)

pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

方式三:pip.conf 文件(推荐)

我个人最常用这种方式,因为可以配置多个备用源:

# ~/.pip/pip.conf (Linux/Mac)
# %APPDATA%\pip\pip.ini (Windows)

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
extra-index-url = 
    https://mirrors.aliyun.com/pypi/simple/
    https://pypi.douban.com/simple/

[install]
trusted-host = 
    pypi.tuna.tsinghua.edu.cn
    mirrors.aliyun.com
    pypi.douban.com
注意:配置了 extra-index-url 后,pip 会同时从多个源搜索包。如果不同源的包版本不一致,可能会安装到非预期的版本。我建议只配一个主源,其他源作为备用。

4.3 依赖冲突的排查与解决

依赖冲突,说白了就是包 A 要 pandas 1.3,包 B 要 pandas 1.5,两个版本不兼容。套利系统里这种问题特别常见,因为涉及金融数据、网络请求、数据库操作,依赖链很长。

4.3.1 如何发现冲突?

pip 在安装时会自动检查依赖关系。如果发现冲突,会报类似这样的错误:

ERROR: Cannot install package-a==1.0 and package-b==2.0 because these package versions have conflicting dependencies.

但有时候 pip 不会报错,只是默默装了一个不兼容的版本。等到运行时才炸。嗯,这里要注意——不要完全相信 pip 的依赖检查

4.3.2 我的排查三板斧

第一板斧:pipdeptree

这个工具能展示依赖树,冲突一目了然:

pip install pipdeptree
pipdeptree

输出示例:

ccxt==4.0.0
  ├── requests>=2.18.0 [required: >=2.18.0, installed: 2.31.0]
  └── setuptools>=60.0.0 [required: >=60.0.0, installed: 68.0.0]
pandas==1.5.3
  ├── numpy>=1.21.0 [required: >=1.21.0, installed: 1.24.3]
  └── python-dateutil>=2.8.1 [required: >=2.8.1, installed: 2.8.2]

看到冲突的包,pipdeptree 会用 *** 标记出来。

第二板斧:pip check

快速检查当前环境有没有冲突:

pip check

如果有冲突,会列出具体是哪些包在打架。

第三板斧:手动分析

当自动工具解决不了时,就得手动来。我曾经遇到过一个 case:ccxt 依赖 requests 2.x,但另一个包依赖 requests 1.x。两个版本 API 不兼容。

解决思路是这样的:

  1. 先看哪个包是核心依赖(比如 ccxt 是套利系统的核心)
  2. 再看冲突的包能不能升级/降级
  3. 如果都不行,考虑用虚拟环境隔离

4.3.3 冲突解决实战

假设我们遇到这样一个冲突:

# 包A 需要 pandas<1.5
# 包B 需要 pandas>=1.5

解决方案:

# 方案一:升级包A到支持pandas 1.5的版本
pip install "package-a>=2.0"

# 方案二:降级包B到支持pandas 1.4的版本
pip install "package-b==1.0"

# 方案三:如果都不行,用虚拟环境隔离
python -m venv env_a  # 跑包A的环境
python -m venv env_b  # 跑包B的环境
核心原则:能升级就不降级,能降级就不隔离。虚拟环境是最后的手段,因为会增加运维复杂度。

4.4 知识体系总览

下面这张图,把依赖管理的核心逻辑串起来了:

依赖管理核心流程 requirements.txt pip 镜像源配置 依赖冲突解决 手动维护 vs pip freeze 版本范围:>=1.3.0,<2.0.0 多文件管理:base/dev/prod 全局配置:pip config set 临时指定:-i 参数 pip.conf 多源配置 pipdeptree 依赖树分析 pip check 快速检查 升级/降级/隔离三选一 目标:可复现、可维护、无冲突的依赖环境

4.5 实战建议

最后,分享几个我在实战中总结的经验:

  • 每次部署前跑一遍 pip check,别等到线上出问题再排查
  • requirements.txt 要提交到 Git,但不要提交 .venv 目录
  • 用 pip freeze > requirements.txt 之前,先手动删掉那些间接依赖
  • 镜像源配置写在项目文档里,新同事来了直接复制粘贴
一个小技巧:在 CI/CD 流程里加一步依赖检查。我习惯在 Jenkins pipeline 里加 pip check 命令,一旦发现冲突就中断构建。这样能提前发现问题,而不是等到部署完才炸。

依赖管理看起来是小事,但做不好就是大坑。你想想看,一个套利系统跑着跑着突然报 ImportError,那损失可不是闹着玩的。把这套流程规范起来,后面能省很多心。


公众号:蓝海资料掘金营,微信deep3321