Featured image of post UV如何使用

UV如何使用

我操了老铁,这uv怎么用

你需要知道的

astral-sh/uv

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创建项目

在你想要创建项目的目录下,运行以下命令:

1
2
3
mkdir my_spider
cd my_spider
uv init

可以看到,uv会自动帮你初始化一个项目,包含git,pyproject.toml,uv.lock等文件。

运行项目

使用vs code打开项目,你会发现uv已经为你写了一个简单的main.py 运行项目 uv run main.py,你会看到以下输出:

1
2
3
4
uv run main.py
Using CPython 3.10.16
Creating virtual environment at: .venv
Hello from my-spider!

写入代码

main.py中添加以下内容:

1
2
3
4
5
6
7
8
9
import httpx

def main():
    print("Hello from my-spider!")
    response = httpx.get("https://www.baidu.com")
    print(response.status_code)

if __name__ == "__main__":
    main()

此时我们再次运行项目 uv run main.py,你会发现程序报错了:

1
2
3
4
Traceback (most recent call last):
  File "C:\dev\my_spider\main.py", line 1, in <module>
    import httpx
ModuleNotFoundError: No module named 'httpx'

添加httpx依赖

我们需要安装httpx依赖,使用以下命令:

1
uv add httpx

uv会自动将httpx添加到pyproject.tomluv.lock中,并且会自动安装httpx依赖。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Resolved 9 packages in 709ms
Prepared 2 packages in 275ms
Installed 8 packages in 112ms
 + anyio==4.13.0
 + certifi==2026.4.22
 + exceptiongroup==1.3.1
 + h11==0.16.0
 + httpcore==1.0.9
 + httpx==0.28.1
 + idna==3.13
 + typing-extensions==4.15.0

💡如果你因为网络环境问题导致下载速度过慢/无法下载,请先看后文 配置镜像

再次运行程序

再次运行程序 uv run main.py,你会发现程序成功运行了:

1
2
3
Using CPython 3.10.16
Hello from my-spider!
200

虚拟环境

在上文中,我们使用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来安装依赖了,比如:

1
uv pip install requests

此时,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 venv to create an environment, or pass --system to install into a non-virtual environment

使用uv venv

在上文中,我们尝试使用uv pip install在全局环境中安装包时,uv提示我们没有找到虚拟环境,并且建议我们使用uv venv来创建一个虚拟环境,或者使用--system参数来安装到非虚拟环境中。

而uv venv则是用来取代virtualenv、venv等工具的,使用uv venv来创建一个虚拟环境非常简单,只需要在项目根目录下运行以下命令:

1
uv venv

使用 uv sync

一些情况下,你可能clone了别人的项目,或者你在别的电脑上开发了一个项目,这时你需要将项目的依赖安装到你的电脑上来运行项目,这时你就可以使用uv sync命令了。 uv sync会根据项目根目录下的pyproject.tomluv.lock文件来安装依赖,并且会自动使用当前项目下的虚拟环境来安装依赖。

一些常见的问题

uv这些命令之间的关系

可能你已经发现了,uv有很多命令,比如uv runuv adduv pipuv 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 pippip 的区别

事实上,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中添加以下内容:

1
2
3
[[tool.uv.index]]
name = "tuna"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"

设置全局的pypi索引

Windows

按下 Win + R,输入 %APPDATA%\uv\uv.toml,回车后在打开的文件中添加以下内容:

1
2
3
[[index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
Linux/MacOS

在你的用户目录下创建一个uv文件夹,在该文件夹下创建一个uv.toml文件,添加以下内容:

1
2
3
[[index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true

如何为某个包指定pypi索引

以torch为例,你需要在pyproject.toml中添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[tool.uv.sources]
torch = [
  { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
]
torchvision = [
  { index = "pytorch-cu128", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
]

[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
# 或是使用镜像源 url = "https://mirrors.nju.edu.cn/pytorch/whl/cu126"
explicit = true

[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 的包管理工具,他很好用。但是如果形成了下列的依赖链条

1
A -> B -> C -> D

当我们安装 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可能也是一个不错的选择。

使用 Hugo 构建
主题 StackJimmy 设计