你需要知道的
uv 是一个非常强大的工具,可以工程化一个python项目,而不只是作为一个虚拟环境管理工具。1
在此我们将以亲手写一个爬虫项目的方式来介绍一下uv的使用方法,当然了,uv的功能远不止这些。
💡此处我们假定你已有python编写经验,并具有使用过诸如requests等第三方库的经历,当然,0基础也是能看懂的
安装UV
安装uv非常简单,可以一键安装
- linux/MacOS:
curl -LsSf https://astral.sh/uv/install.sh | sh - windows:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
在Windows系统下,你可能需要重启终端或PowerShell来让uv生效。
安装完成后,你可以在终端中输入uv --version来查看uv的版本,如果显示了版本号,说明安装成功了。
撰写项目
使用uv创建项目
在你想要创建项目的目录下,运行以下命令:
| |
可以看到,uv会自动帮你初始化一个项目,包含git,pyproject.toml,uv.lock等文件。
运行项目
使用vs code打开项目,你会发现uv已经为你写了一个简单的main.py
运行项目 uv run main.py,你会看到以下输出:
| |
写入代码
在main.py中添加以下内容:
| |
此时我们再次运行项目 uv run main.py,你会发现程序报错了:
| |
添加httpx依赖
我们需要安装httpx依赖,使用以下命令:
| |
uv会自动将httpx添加到pyproject.toml和uv.lock中,并且会自动安装httpx依赖。
| |
💡如果你因为网络环境问题导致下载速度过慢/无法下载,请先看后文 配置镜像
再次运行程序
再次运行程序 uv run main.py,你会发现程序成功运行了:
| |
虚拟环境
在上文中,我们使用uv run main.py来运行项目,uv会自动为我们创建一个虚拟环境,并且会自动使用这个虚拟环境来运行项目。因此,我们不需要手动激活虚拟环境,也不需要手动安装依赖,只需要使用uv run来运行项目即可。
但是有些时候,我们可能需要进入虚拟环境来进行一些操作,比如安装一些包,或者运行一些命令,这时我们需要手动激活虚拟环境,方法如下:
- Windows:
.\.venv\Scripts\activate - Linux/MacOS:
source .venv/bin/activate
使用uv pip
既然uv那么快,我能不能用uv pip来换掉pip呢?当然可以,uv pip是uv重写的pip,具有pip的功能,但更高效。
在项目里使用uv pip
如果你按照上文的方式创建了一个项目,那么你就可以直接在项目里使用uv pip来安装依赖了,比如:
| |
此时,uv会自动使用当前项目下的虚拟环境来安装requests依赖,但是你会发现,pyproject.toml和uv.lock并没有更新。
这是因为uv pip只是安装了requests依赖,并没有将requests添加到pyproject.toml和uv.lock中。
如果你想要将requests添加到pyproject.toml和uv.lock中,你需要使用uv add requests命令来安装requests依赖。
在conda环境中使用uv pip
如果你电脑同时安装了anacoda和uv,那么你在使用uv pip安装依赖时,uv会自动使用当前激活的anaconda环境来安装依赖,而不是系统的全局环境。
在全局环境中使用uv pip
如果你没有在项目里使用uv pip,而是在全局环境中使用uv pip来安装依赖,那么uv会警告你:不要这么干:
error: No virtual environment found; run
uv venvto create an environment, or pass--systemto install into a non-virtual environment
使用uv venv
在上文中,我们尝试使用uv pip install在全局环境中安装包时,uv提示我们没有找到虚拟环境,并且建议我们使用uv venv来创建一个虚拟环境,或者使用--system参数来安装到非虚拟环境中。
而uv venv则是用来取代virtualenv、venv等工具的,使用uv venv来创建一个虚拟环境非常简单,只需要在项目根目录下运行以下命令:
| |
使用 uv sync
一些情况下,你可能clone了别人的项目,或者你在别的电脑上开发了一个项目,这时你需要将项目的依赖安装到你的电脑上来运行项目,这时你就可以使用uv sync命令了。
uv sync会根据项目根目录下的pyproject.toml和uv.lock文件来安装依赖,并且会自动使用当前项目下的虚拟环境来安装依赖。
一些常见的问题
uv这些命令之间的关系
可能你已经发现了,uv有很多命令,比如uv run、uv add、uv pip、uv venv等等,这些命令之间的关系是这样的:
uv run是用来运行项目的命令,uv会自动使用当前项目下的虚拟环境来运行项目。uv add是用来安装依赖的命令,uv会自动将依赖添加到pyproject.toml和uv.lock中,并且会自动使用当前项目下的虚拟环境来安装依赖。uv pip是用来安装依赖的命令,uv会自动使用当前项目下的虚拟环境来安装依赖,但是uv pip并不会将依赖添加到pyproject.toml和uv.lock中。uv venv是用来创建虚拟环境的命令,uv会在当前项目根目录下创建一个虚拟环境,并且会自动使用这个虚拟环境来运行项目。
或者说 uv add 是一个更高层次的命令,包含了 uv pip 的功能,同时还会更新 pyproject.toml 和 uv.lock 文件,而 uv pip 则是一个更底层的命令,只负责安装依赖,不会更新 pyproject.toml 和 uv.lock 文件,uv venv 则是一个独立的命令,用来创建虚拟环境,不涉及安装依赖的功能。
uv pip 和 pip 的区别
事实上,pip 也只是 Python 的一个包,而不是独立的组件。你甚至可以使用 pip 来安装 pip!
而 uv pip 是 uv 重写的 pip,具有 pip 的功能,但更高效。同时,uv pip 会自动使用当前项目下的虚拟环境来安装依赖,而不是全局环境。
为什么我使用uv创建了虚拟环境,但是在环境里用pip安装的包却失踪了
正如上文所述,pip只是python的一个包,而不是独立的组件。当你在uv创建的虚拟环境中时,由于uv并不会一并安装pip等组件,导致你在虚拟环境中使用pip其实是全局环境中的pip,自然而然,你安装的包就会安装在全局环境中,而不是虚拟环境中。
为什么我的pip包下载速度这么慢
uv并不会使用你系统设置的pip的pypi索引,因此你需要自行为uv设置一个pypi索引,方法如下:
为项目使用单独的pypi索引
在你的项目的pyproject.toml中添加以下内容:
| |
设置全局的pypi索引
Windows
按下 Win + R,输入 %APPDATA%\uv\uv.toml,回车后在打开的文件中添加以下内容:
| |
Linux/MacOS
在你的用户目录下创建一个uv文件夹,在该文件夹下创建一个uv.toml文件,添加以下内容:
| |
如何为某个包指定pypi索引
以torch为例,你需要在pyproject.toml中添加以下内容:
| |
[tool.uv.sources] 可以为某个包指定一个或多个索引,uv会根据条件来选择使用哪个索引来安装该包。
[[tool.uv.index]] 可以为定义一个新索引,explicit = true 表示这个索引只能通过 [tool.uv.sources] 来使用,而不能作为默认索引。
我为什么要使用UV
问这个问题之前,我们先来看看在uv出现之前,Python开发者是如何管理依赖和环境的,以及存在什么样的问题,或者说python项目的发展历程是怎么样的。
全局环境时代
在最早期,python并没有项目化概念,大家都是一个.py文件一个.py文件的写代码,依赖也是直接安装在全局环境中的,这就导致了全局环境污染的问题。
在开发两个不同的项目时,可能会出现两个项目依赖的库版本不兼容的情况。 比如项目A 需要pydantic 2.0,而项目B 需要pydantic 3.0,这时如果直接安装在全局环境中,就会导致冲突,无法同时满足两个项目的需求。
后来出现了虚拟环境工具(如 virtualenv、venv),可以为每个项目创建一个独立的环境,避免了全局环境冲突的问题。但是这些工具在管理依赖和版本方面仍然存在一些不足,比如需要手动激活环境、安装依赖等。
venv时代
此时我们提出了venv,在项目下创建一个虚拟环境,这样就可以避免电脑上所有的项目共用全局环境。因而解决了全局环境时代的依赖冲突问题。但是venv只是一个虚拟环境管理工具,并没有提供依赖管理的功能,因此我们依旧使用pip和requirements.txt来管理依赖,这就导致了所谓的「虚空依赖」,即一些包被安装了但没有被任何项目依赖,最终占用磁盘空间。
此时,我们又出现了poetry、pipenv等工具,这些工具提供了依赖管理的功能。
python 的虚空依赖
pip 是 Python 的包管理工具,他很好用。但是如果形成了下列的依赖链条
| |
当我们安装 A 时,pip 会自动安装 B、C、D 这三个依赖包。 但是当我们卸载 A 时,pip 只会卸载 A,而不会自动卸载 B、C、D 这三个依赖包。 这就导致了所谓的「虚空依赖」,即一些包被安装了但没有被任何项目依赖,最终占用磁盘空间。
项目化
后来,python有了编写大型项目的能力,但是迟迟没有一个像样的规范来指导我们如何去编写一个python项目,直到pyproject.toml的出现,才让python项目有了一个统一的规范。
pyproject.toml提出自PEP 518,定义了一个标准的项目配置文件,包含了项目的元数据、依赖、构建系统等信息。它的出现让python项目有了一个统一的规范,也让python项目的管理变得更加简单和高效。
uv出现
往常,poetry、pdm、pipenv等工具虽然提供了依赖管理的功能,但是在性能和功能上都存在一些不足,比如安装依赖的速度慢、无法管理多个项目的依赖等问题。
uv着重解决了这些问题,提供了一个高效、全面的项目管理工具,可以替代pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv等工具,同时还提供了很多其他的功能,比如运行脚本、管理Python版本、支持Cargo风格的工作区等。
为什么不用conda?
在py环境管理方面,我认为主要有两大流派,一个是conda,一个是venv。uv,poetry等工具都是venv流派的,而anaconda/miniconda则是conda流派的。
事实上,我并不喜欢conda,由于其过于重磅,侵入终端,以及包办一切。而在环境打包上,conda的环境打包功能也不如venv流派的工具来得好用和高效。
conda导出的环境是一个yaml文件,里面包含了所有的依赖包和版本信息,但是这个yaml文件并不能通用,因为它包含了很多conda特有的信息,比如平台、渠道等,这些信息在其他环境中可能并不适用。
而venv流派的工具导出的环境是一个标准的requirements.txt或者pyproject.toml文件,这些文件是通用的,可以在任何环境中使用。
但是conda也有优点,比如它提供了一个完整的生态系统,包含了很多科学计算和数据分析的包,这些包在venv流派的工具中可能并不容易安装和管理。因此,如果你需要使用这些包,或者你喜欢conda的生态系统,那么conda可能也是一个不错的选择。
