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
还有一种场景——你开发环境和生产环境依赖不一样。比如开发需要 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
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 不兼容。
解决思路是这样的:
- 先看哪个包是核心依赖(比如 ccxt 是套利系统的核心)
- 再看冲突的包能不能升级/降级
- 如果都不行,考虑用虚拟环境隔离
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 知识体系总览
下面这张图,把依赖管理的核心逻辑串起来了:
4.5 实战建议
最后,分享几个我在实战中总结的经验:
- 每次部署前跑一遍 pip check,别等到线上出问题再排查
- requirements.txt 要提交到 Git,但不要提交 .venv 目录
- 用 pip freeze > requirements.txt 之前,先手动删掉那些间接依赖
- 镜像源配置写在项目文档里,新同事来了直接复制粘贴
pip check 命令,一旦发现冲突就中断构建。这样能提前发现问题,而不是等到部署完才炸。
依赖管理看起来是小事,但做不好就是大坑。你想想看,一个套利系统跑着跑着突然报 ImportError,那损失可不是闹着玩的。把这套流程规范起来,后面能省很多心。
公众号:蓝海资料掘金营,微信deep3321