用 uv 替代 pip + venv:一次 Python 依赖管理的迁移实践
为什么想换掉 pip + venv
团队里有个跑在 2C2G 小机器上的数据同步脚本,依赖十几个包。requirements.txt 里只锁了顶层依赖,某天 pydantic 发了个小版本,脚本在凌晨三点挂了——因为 pydantic-core 的编译产物和机器上的 Python 3.11.6 不匹配。这类问题的根源不是 pip 本身不好,而是「环境重建慢」加「依赖解析不确定」这两件事叠在了一起。
uv 是 Astral 用 Rust 写的 Python 包管理器,它同时做三件事:解析依赖、安装包、管理虚拟环境。我花了两个下午把三个项目迁过去,记录一下过程和踩到的坑。
一、安装
Windows 下用官方脚本最省事:
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
Linux / macOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
装完 uv --version 能出版本号就行。它是单文件二进制,本身不依赖 Python 运行时。
二、从 requirements.txt 迁移
这一步 uv 做得比我预期好。进入项目目录,先建环境再同步:
uv venv --python 3.11
uv pip install -r requirements.txt
注意 uv venv 默认不会帮你激活环境,Linux 下要 source .venv/bin/activate,Windows 是 .venv\Scripts\activate。
速度差异很明显:同一份 requirements.txt,pip 在本机冷装 68 秒,uv 用了 9 秒。原因是 uv 有一份全局的本地缓存,同一个 wheel 只下载一次,后续项目直接硬链接过去。
三、换成 pyproject.toml + uv.lock
真正的收益在锁文件。老项目可以这样转:
uv init --bare
uv add flask pydantic requests
uv 会把依赖写进 pyproject.toml,并生成 uv.lock。这个 lock 文件是跨平台的,记录了每个包的哈希和全部传递依赖,比 requirements.txt 严格得多。之后在服务器上只跑:
uv sync --frozen
--frozen 表示完全按 lock 文件来,不允许重新解析。这一点很关键:它把「能不能装成功」从运行时的不确定性,变成了构建期的确定性。
四、踩到的三个坑
-
私有源配置。uv 不认识 pip.conf,要在 pyproject.toml 里写
[[tool.uv.index]],或者用环境变量UV_INDEX_URL。我第一次忘了设,它绕去公网拉包,超时十几秒才报错,排查了很久。 -
系统包依赖。像 psycopg2 这类需要编译的包,uv 会优先找预编译 wheel,找不到就本地编译,这时候 gcc 和 python3-dev 还是得装。uv 只是加速,不解决编译环境问题。
-
和 poetry 项目共存。同一个仓库里既有 poetry.lock 又有 uv.lock 会互相干扰,CI 里必须明确走哪一条。我们最后是把 poetry 那段直接删了。
五、值不值得迁
如果项目只有三五个依赖、环境一年重建一次,pip + venv 完全够用,迁移收益不明显。
但下面任一条成立,就值得迁:
- 依赖树深(超过 30 个传递依赖),pip 解析经常给出不同结果
- CI 每次都要重装依赖,装包时间占构建时间一半以上
- 需要跨平台一致(开发在 Windows、部署在 Linux)
三个项目迁完之后,CI 里装依赖那一步从平均 74 秒降到 11 秒。最直接的感受是 PR 的反馈变快了。
写在最后
uv 不是银弹。它解决的是「解析与安装的效率加确定性」,不解决依赖版本本身选得对不对。lock 文件能锁住 pydantic 的版本,但如果一开始就挑了个不兼容的次版本,锁得再死也是错的。工具能压缩的是工程摩擦,不是判断力。
- 点赞
- 收藏
- 关注作者
评论(0)