「TF」02 为什么我选择`uv`?(个人经验分享)
Greetings! ¶
嗨!这里是阿乎的博客!(> ▽ <) /~
承接上文,为什么我会与uv
,这个被称为“下一代Python包管理器”的工具相遇呢?
为什么我会在熟悉conda
和pip
的情况下,仍然选择接触,甚至拥抱uv
呢?
1. uv
—— 注定的相遇 ¶
在日常的学习和工作中,我一直使用conda
作为Python的包管理器和环境管理器。
然而,在浏览一些GitHub项目时,我发现越来越多的项目中没有requirements.txt
,而是多了一个叫作pyproject.toml
的文件。
toml
(Tom’s Obvious, Minimal Language)是一种简洁易读的配置文件格式。我也认为其是未来平替,甚至超越一些传统配置文件格式(如json
、yaml
等)的有力竞争者。
它看起来像这样:
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
dependencies = [
"xxxx",
"yyyy",
"zzzz"
...
]
于是,在查找资料后,我发现这个pyproject.toml
文件是由PEP 518
引入的,旨在为Python项目提供一个统一的构建系统接口,而uv
对其有着专门的支持。同时,它也是uv
默认的项目配置文件。
至此,我对这个熟悉却陌生的虚拟环境管理工具产生了浓厚的兴趣。
2. 我为何拥抱uv
? ¶
在学习并使用uv
一段时间后,我发现它相比于我熟悉的conda + pip
组合,有着一些显著的优势,同时也存在着一些不足。
2.1. 不用再记环境名了! ¶
在使用conda
时,我需要为每一个项目创建一个新的环境,并记住这个环境的名称。每次要使用某个项目时,都需要激活对应的环境。
比如说:
conda create -n a_random_env_name python=3.10
conda activate a_random_env_name
而时间一长,我们会无可避免地创建出一堆不同的环境。如果我忘记了环境名,那么我得去查找我创建的环境列表,找到对应的环境名,然后再激活它。
conda env list
对于我这种“健忘”的选手,有时候确实会在一长串环境列表中犯糊涂——“这个项目用的到底是myenv
还是new_env
啊?😵”
我的解决方法:我通常会创建一个叫作
misc
的环境,用来杂乱地装一些通用的依赖,并将其作为各种不值得多创建一个环境但是又需要安装额外依赖的项目的环境。
而如果使用uv
,整个虚拟环境都是放在项目目录下的.venv
文件夹中。只要配置好依赖,就可以直接使用环境中的Python解释器和包来运行项目。
uv run xxx.py
对我来说可真是救大命了!😋
2.2 极致的环境安装体验! ¶
根据官方的说法,uv
有着远超于传统工具的安装速度。
然而让我最喜欢的不只是速度,还是这一条简单的命令:
uv sync
它会根据pyproject.toml
和uv.lock
文件,自动创建并同步虚拟环境中的依赖!简直就是:All in one!
uv.lock
文件会锁定依赖的具体版本,确保每次安装的依赖版本一致,避免因版本差异导致的问题。
值得一提的是,在使用传统工具安装torch
时,如果不在命令后添加下载源,那么其默认安装的是cpu
版本的torch
。
为了避免
torch.cuda.is_available()
返回False
,我总是需要在安装前访问官网,复制下--index-url https://download.pytorch.org/whl/cu1xx
添加到pip install
命令后面。
不过,有了pyproject.toml
后,我们可以手动设置部分包的下载源——也就是说,这次,我们可以直接指定安装cuda
版本的torch
了!
只需要把下面这两段加到pyproject.toml
末尾即可。
[tool.uv.sources]
torch = { index = "pytorch-cu128" }
torchvision = { index = "pytorch-cu128" }
[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
explicit = true
整个文件类似下面这样:
[project]
name = "xxx"
version = "0.1.0"
description = "xxx"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"torch>=2.7.0",
"torchvision>=0.22.0",
]
[tool.uv.sources]
torch = { index = "pytorch-cu128" }
torchvision = { index = "pytorch-cu128" }
[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
explicit = true
2.3 生态趋势与防御式学习 ¶
无论是在科研还是工业领域,我逐渐发现一个趋势:
越来越多的新项目开始把 uv
作为默认的包管理工具,甚至有些仓库不再提供传统的 requirements.txt
或 conda 安装说明,而是直接放了一个 pyproject.toml
。
这让我意识到,如果我还停留在“conda + pip”的组合,将来一定会遇到障碍:
—— 想尝试一个新项目,却因为缺乏 uv
的经验而卡在起点。
所以,对我而言,学习并拥抱 uv
不仅仅是“喜欢它的体验”,更是一种“防御式”的选择。
我提前把 uv
纳入日常使用,逐步适应它的工作流。这样一来,即使未来 Python 生态真的全面转向 uv
,我也能从容应对,不会手忙脚乱。
2.4 兼容性与“向下兼容”的安心感 ¶
在第一次尝试 uv
之前,我心里其实有个疑问:
“如果我用了
uv
,那我的同学、同事、导师还能跑我的项目吗?”
毕竟大家大多数还停留在 conda + pip
的组合,如果我突然甩给他们一个 pyproject.toml
,他们会不会一脸问号?
好消息是:uv
在兼容性这块做得相当贴心。
除了支持 pyproject.toml
和自己的 uv.lock
之外,它也能轻松生成一个 requirements.txt
文件。这样一来,即使我的项目是用 uv
搭建的,别人依旧可以用 pip 或 conda 来安装依赖,丝毫不会影响协作。
这让我在选择 uv
的时候更有底气:
我可以拥抱新工具的便利,但不会把别人“绑架”进来。
2.5 包与解释器的复用:更省空间 ¶
过去用 conda
,我的硬盘里常常堆着一堆环境。
虽然 conda 有所谓的“硬链接”机制,可以在环境之间共享相同版本的包文件,但一旦版本稍有不同,就得重新装一份,空间很快就被吃满。
uv
的做法更直接:
- 它有全局缓存机制(global cache),同一个版本的包只会下载/构建一次;
- 当多个项目需要这个包时,直接从缓存里拿,不会重复浪费空间;
- 虚拟环境本身依然是隔离的,但底层的 wheel 文件、解释器都能得到复用。
举个例子:
我在两个项目里都用到了 numpy==1.26.0
。第一次 uv sync
会去下载,但第二次在另一个项目里执行时,几乎是秒装 —— 因为已经从缓存里直接复用。
这种“只装一次,到处可用”的感觉,实在太舒服了。😋
3. 总结 ¶
所以,与其说“为什么选择 uv
”,不如说“为什么不选择 uv
”?
对我来说,uv
带来了更轻便的日常开发体验,也让我能更好地适应未来的生态趋势。
当然,conda 在科研和团队合作里依然有不可替代的地位,比如处理一些非 Python 的依赖时,它仍然更强大。
换句话说:我并不是完全抛弃 conda,而是把 uv
当作我更多场景下的“首选方案”。😎