我第一次用 pip install 的时候,觉得这就是标准答案了。
pip install requests,回车,等几秒,装好了。
还能怎么样呢?
后来项目多了,虚拟环境多了,依赖冲突多了,我才发现 pip 只是"能用来装包",不是"能管好依赖"。
pip + requirements.txt 的痛,谁用谁知道
最开始我的项目是这样的:
# requirements.txt
requests
pandas
numpy
scikit-learn
看起来没问题。
直到同事拉了我的代码,pip install -r requirements.txt,跑起来报错。
原因是:我装的是 pandas==1.5.3,同事装的是 pandas==2.1.0,API 有变动,代码不兼容。
requirements.txt 不写版本号,别人装的时候可能装到不兼容的版本。写版本号,你又要手动维护,升级某个包的时候,它的依赖包版本也要跟着改,改错了就炸。
后来我学会了用 pip freeze:
pip freeze > requirements.txt
这下版本号全锁定了,同事装出来和我一模一样。
但新问题来了:pip freeze 会把所有间接依赖也写进去,一个简单项目的 requirements.txt 能有上百行,长得像这样:
certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
requests==2.31.0
urllib3==2.2.1
# ...还有 50 行你不知道哪来的
你根本不知道哪些是你直接用的,哪些是别人带进来的。
poetry 的出现,让我觉得"这才对嘛"
poetry 的核心思想是:你只需要声明你直接依赖什么,版本范围可以写宽松一点,poetry 帮你算出来一个所有包都兼容的组合,然后锁死。
pyproject.toml(你告诉 poetry 你要什么):
[tool.poetry]
name = "my-project"
version = "0.1.0"
[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.31.0"
pandas = "^2.0"
poetry.lock(poetry 算出来的精确版本,提交到 git):
[[package]]
name = "requests"
version = "2.31.0"
dependencies = [
"charset-normalizer",
"idna",
"urllib3",
]
别人拉了代码,运行 poetry install,装出来的环境和你一模一样。
我用 poetry 用了两年,整体很满意。依赖解析比 pip 靠谱,虚拟环境自动管理,发布到 PyPI 也方便。
但有一个问题:慢。
poetry 的慢,是那种让人烦躁的慢
poetry install 跑一次,依赖多的话要等一两分钟。
它在做什么?在解析依赖树,找所有包之间兼容的版本组合。这个计算是复杂的,特别是当你有很多依赖的时候。
我有一个项目,依赖了 60 多个包,poetry install 要跑将近两分钟。
每次换机器、每次 CI/CD,都要等这两分钟。
后来我遇到了 uv。
uv 是什么,为什么这么快
uv 是一个用 Rust 写的 Python 包管理工具,作者是 Astral 团队(就是做了 ruff 那个团队)。
它的核心卖点:快。
快到什么程度?
同一个项目,poetry install 要 90 秒,uv pip install 只要 5 秒。
我第一次跑的时候以为它没装完,检查了一下,包都在,环境没问题。
原因有几个:
- Rust 写的,性能天然比 Python 写的工具高
- 依赖解析用并行计算
- 缓存机制做得非常好,重复安装几乎秒完成
安装 uv 只需要一行:
curl -LsSf https://astral.sh/uv/install.sh | sh
或者如果你用 pip(最后一次用 pip 装工具):
pip install uv
我从 poetry 迁移到 uv 的过程
uv 支持多种工作模式,你可以把它当 pip 的替代品,也可以让它完全接管项目管理。
最简单的方式,用 uv 管理虚拟环境 + 装包:
# 创建虚拟环境
uv venv
# 激活(和平时一样)
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows
# 装包,速度飞快
uv pip install requests pandas numpy
如果你有 requirements.txt:
uv pip install -r requirements.txt
体验上和 pip 几乎一样,但速度快 10-100 倍。
uv 的进阶用法:完全替代 poetry
uv 也可以管理 pyproject.toml 和锁文件,彻底替代 poetry:
# 初始化项目
uv init my-project
# 添加依赖
uv add requests
uv add pandas --group dev
# 同步环境(根据 lock 文件安装)
uv sync
uv init 创建的项目结构:
my-project/
├── pyproject.toml
├── uv.lock
└── src/
└── my_project/
└── __init__.py
uv.lock 和 poetry.lock 类似,但生成速度快得多。
我迁移了一个有 60 多个依赖的项目,从 poetry install 的 90 秒,到 uv sync 的 8 秒。
8 秒。
一个完整的对比数据
我用同一个项目(FastAPI + 各种数据处理库,约 80 个依赖包)测试:
| 操作 | pip | poetry | uv |
|------|-----|--------|-----|
| 首次安装 | 45秒 | 98秒 | 6秒 |
| 有缓存安装 | 12秒 | 35秒 | 2秒 |
| 添加新依赖 | 8秒 | 28秒 | 1秒 |
| 创建虚拟环境 | 手动 | 自动 | 0.3秒 |
数据说明一切。
但我不是来说 uv 是最好的
工具选型永远要看场景。
如果你已经在用 poetry,项目跑得好好的,没必要为了快那几十秒去迁移。
如果你是新项目,或者受够了 poetry 的慢,uv 值得一试。
如果你在维护一些老项目,只用 requirements.txt,uv pip install 可以无缝替换 pip install,立刻感受到速度提升,没有任何迁移成本。
我现在的状态是:新项目直接用 uv init,老项目把 pip install 换成 uv pip install。
一个冷知识:uv 还能管理 Python 版本
之前要用 pyenv 或者手动装多个 Python 版本,现在 uv 一键搞定:
# 安装 Python 3.12
uv python install 3.12
# 用 Python 3.12 创建虚拟环境
uv venv --python 3.12
一条工具链搞定包管理 + Python 版本管理,这才是现代 Python 开发该有的体验。
说到底,工具变迁的背后是同一个逻辑:你在等电脑的那几十秒、几分钟,累加在一起,是实打实的生命。
从 pip 到 poetry 到 uv,我不会说"早用早享受"这种话,但如果你也受够了等 poetry install 的那两分钟,uv 可能会让你觉得,这几年的等待其实是可以避免的。
你用什么管理 Python 依赖?还在用 pip 裸写 txt 吗?欢迎留言区聊聊你的工具链,说不定有惊喜发现。
最后,别忘了关注「有为大青年」,我们下期见~