---
url: /article/20250717/index.md
---
「Rust 重写一切，UV 管理 Python 的一切。」在 Python 生态中，包管理一直是一个复杂且多样化的问题。随着项目的增长和依赖关系的增加，管理这些包变得越来越具有挑战性。UV 作为一个新兴的包管理工具，旨在简化这一过程，并提供更高效的解决方案。

Windows 用户使用管理员模式的 PowerShell 执行下面的命令安装即可，如果有报错问问 GPT 就可以了，多半是权限的问题。

```shell
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

其他方式和操作系统安装参考：<https://docs.astral.sh/uv/getting-started/installation/#standalone-installer>

## 简单尝试

### Python 管理

`uv python`+ 命令，一般是先 `list` 看哪些版本可以下，`find` 看安装了什么版本，`install` 安装，`uninstall` 卸载，`pin` 特定版本到项目。

安装最新版 cpython

```shell 是
uv python install [版本号] --reinstall(可选，覆盖安装)
```

升级 python 版本

```shell
uv python upgrade
```

### 代理执行

:::note
前提是.py 文件中导入的包为 [标准类库](https://docs.python.org/3/library/index.html)，例如 `import os`、`import sys` 等等
:::

```shell
uv run <python 文件 > [参数]
```

有特定依赖项使用以下命令：

```shell
uv run <python 文件 > --with < 依赖项 > [参数]
```

### 显式声明依赖

> 2023 年 10 月，python 官方发布了 [PEP 723](https://peps.python.org/pep-0723/)，引入了声明式内敛元数据以及 `pyproject.toml` 文件来声明项目的依赖项。

初始化

```shell
uv init --script <python 文件 > --python <python 版本 >
```

声明脚本依赖，这里向 `example.py` 文件添加了 `requests` 和 `rich` 这两个依赖项，版本限制为小于 3 的版本。

```shell
uv add --script example.py 'requests<3' 'rich'
```

### 修改下载地址（包索引）

如果你想使用其他镜像源，可以通过以下命令修改包索引地址：

```shell
uv add --index "https://example.com/simple" --script example.py 'requests<3' 'rich'
```

会一并写入 `pyproject.toml` 文件中。

```toml
# [[tool.uv.index]]
# url = "https://example.com/simple"
```

到这里，我们就可以看出它的长处了，可以显示定义下载源，版本依赖，甚至可以指定脚本文件。

### 锁定脚本依赖

与项目不同，脚本依赖是指特定脚本所需的依赖项。可以通过以下命令锁定脚本依赖：

```shell
uv lock --script example.py
```

生成的结构如下

@[code-tree title="Example" height="400px" entry="test.py.lock"](docs/.vuepress/public/code/20250717/test)

### 使用不同 python

```shell
uv run <python 文件 > --python <python 版本 > [参数]
```

## UV 工具

### uvx

只有当在无关紧要的测试、扁平化项目中用这个比较合适，`uvx` 等价于下面的命令：这样做是为了预先测试检查兼容 python 包版本，你知道的，python 的包管理就和重庆的天气一样糟糕（

```shell
uv tool run
```

```shell
$ uvx pycowsay nb

  --
< nb >
  --
   \   ^__^
    \  (oo)\_______
       (__)\       )\/\
           ||----w |
           ||     ||

```

> 这个用的比较少，暂时写到这里。链接：<https://docs.astral.sh/uv/guides/tools/#running-tools>

## 项目管理  常用&#x20;

```shell
uv init hello-world
cd hello-world
```

生成的目录结构如下：

@[code-tree title="hello-world" height="400px" entry="main.py"](docs/.vuepress/public/code/20250717/hello-world)

### 项目结构

::: file-tree

* ++ .venv
  * Scripts
    * include ## 包含的头文件
    * lib ## 所有的包安装位置
    * ...
    * python.exe ## 当前版本 python 执行程序
  * pyvenv.cfg
* .python-version
* README.md
* main.py
* pyproject.toml
* uv.lock
  :::

#### pyproject.toml

> 查看 [所有字段](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/)

```toml title="pyproject.toml"
[project]
name = "hello-world" ## 项目名称
version = "0.1.0" ## 项目版本
description = "Add your description here" ## 项目描述
readme = "README.md" ## 项目说明文件
requires-python = ">=3.13" ## 需要的 python 版本
dependencies = [] ## 依赖项列表
```

现在我们来试试 [安装 yolo](https://docs.ultralytics.com/zh/quickstart/) 的依赖项，PYPI 的镜像站点下载最好把代理关了或者设置镜像。

```shell title="install.sh"
uv add ultralytics
```

![20250719.png](/assets/20250719.png)

非常快，10 秒内完成几百兆的文件下载和索引！（不过网速足够可能才行）

变化如下：

```toml title="pyproject.toml"
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "matplotlib>=3.10.3", # [!code ++]
    "tqdm>=4.67.1", # [!code ++]
    "ultralytics>=8.3.168", # [!code ++]
]
```

#### .python-version

告诉项目使用的 Python 版本号，初始化后自带，也可自定义

#### .venv

项目的虚拟环境，即与系统其余部分隔离的 Python 环境。这是 uv 将安装项目依赖项的位置。

#### uv.lock 重要&#x20;

uv.lock 是一个跨平台的 lockfile，其中包含有关项目依赖项的确切信息，类似与 npm 中的 `package.lock` 或 `pnpm.lock`, 由 uv 自动化控制和生成，不要修改它。它可以：

* 声明项目的开发平台
* 声明项目所需的 Python 版本
* 声明包及其依赖的下载地址、文件哈希等

:::note 文件哈希

通过哈希校验算法确认包未被篡改，保障包的完整性和安全性。

:::

这样做的好处是让不同平台的开发者在安装依赖时获得相同的结果，确保项目在不同环境中的一致性。（哇，安装包很头疼的好吗？）

坏处是若下载地址失效或包被删除，可能会导致安装失败，可以 \[手动尝试修改下载地址]\(/article/20250717/# 修改下载地址 - 包索引)。

````toml title="pyproject.toml"

```lock title="uv.lock"
version = 1
revision = 2
requires-python = ">=3.13"
resolution-markers = [
    "sys_platform == 'win32'", 
    "sys_platform == 'darwin'",
    "platform_machine == 'aarch64' and sys_platform == 'linux'",
    "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32')",
]

[[package]]
name = "certifi"
version = "2025.7.14"
source = { registry = "https://mirrors.cernet.edu.cn/pypi/web/simple" }
sdist = { url = "https://cmcc.mirrors.ustc.edu.cn/pypi/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" }
wheels = [
    { url = "https://cmcc.mirrors.ustc.edu.cn/pypi/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" },
]
````

### 添加、移除依赖

> add、remove

```shell
uv add requests
uv remove requests

# Specify a version constraint
uv add 'requests==2.31.0'

# Add a git dependency, 手动指定
uv add git+https://github.com/psf/requests
```

### 从 requirements.txt 中同步

在常见的 pip 管理的项目中，我们会用到 `requiremts.txt` 来管理项目，uv 支持迁移

```shell
uv add -r requirements.txt -c constraints.txt
```

### 升级某个包

正常的流程是先锁定其他包的版本，再执行某个包的升级任务

```shell
uv lock --upgrade-package <package>
```

### 查询项目版本

```shell
uv version

hello-world 0.1.0

uv version --short

0.1.0

uv version --output-format json

{
  "package_name": "hello-world",
  "version": "0.1.0",
  "commit_info": null
}

```

### 同步依赖

一般而言，写好代码后为了运行起来，新增的依赖我们自然而然就把依赖装好了，比如 `uv add`。但是如果是团队协作中，其他人可能没有安装你新增的依赖（反之亦然），这时候就需要同步依赖了。

```shell
uv sync # [!code warning] 同步依赖到 uv.lock 和 pyproject.toml
.venv\Scripts\activate ## windows
source .venv/bin/activate ## linux/macOS
flask run -p 3000
python example.py
```

:::note
激活环境后，无需再使用 `uv` 前缀，直接使用 `python` 命令即可运行脚本。
:::

### 造轮子

```shell
uv build
```

默认在当前目录生成 `dist` 目录，里面包含了打包好的文件。

:::file-tree title="dist" collapsed=true

* .venv
  * Scripts
    * include
    * lib
    * python.exe
  * pyvenv.cfg
* ++ dist
  * hello\_world-0.1.0-py3-none-any.whl // wheel 包，也就是所谓的轮子
  * hello\_world-0.1.0.tar.gz // 源码包
  * hello\_world.egg-info // hutch 包信息
    * dependency\_links.txt
    * PKG-INFO
    * requires.txt
    * SOURCES.txt
    * top\_level.txt
* main.py
* pyproject.toml
* uv.lock
* README.md
* .python-version
* .gitignore
* LICENSE
  :::

## 发布包

### 升级项目版本号

```shell
uv version n.e.w
```

平级升级，uv 划分的标准从大到小为：major, minor, patch, stable, alpha, beta, rc, post, and dev

```shell
# 从稳定版到预发布版
uv version --bump patch --bump [alpha/beta/rc/post/dev]

# 预发布版的内部升级
uv version --bump [alpha/beta/rc/post/dev]

# 预发布版到稳定版
uv version --bump stable
```

:::note 同步注意
执行 `uv version` 后会自动更新 `pyproject.toml` 和 `uv.lock` 文件中的版本号。不升级请执行 `--frozen` 参数
:::

[//]: # "TODO: 写一下发布包的流程 https://docs.astral.sh/uv/guides/package/#publishing-your-package https://docs.pypi.org/trusted-publishers/adding-a-publisher/#github-actions"

[//]: # "### 发布到 Github"

[//]: #

[//]: # "### 发布到 PyPI"

[//]: #

[//]: # "### 发布到 Gitlab"

## 迁移

### pip2uv

我们都知道，pip 使用 requirements.txt 来管理依赖。例如，写入当前项目的依赖使用：

:::note 环境隔离

考虑使用 venv 虚拟环境来隔离项目依赖，避免某个 Python 版本全局安装包的冲突（想哭了😭）。

```shell
python -m venv
source .venv/bin/activate
pip ...
```

:::

其中一种管理依赖的方式是 `pip-compile` 编译规则集 requirements.in [+requirements.in] 文件

[+requirements.in]: 在项目最容易出现兼容性问题的地方最好测试人员写一个这样的文件，手动控制特别软件包的版本。

例如，`requirements.in` 文件内容如下：

```text title="requirements.in"
fastapi
pydantic>2
```

表示 `fastapi` 无版本约束， `pydantic` 版本大于 2.0.0。

执行下面的命令来生成 `requirements.txt` 文件：

```shell
pip-compile requirements.in -o requirements.txt
```

```text title="requirements.txt"
annotated-types==0.7.0
    # via pydantic
anyio==4.8.0
    # via starlette
fastapi==0.115.11 
    # via -r requirements.in // [!code warning]
idna==3.10 
    # via anyio
pydantic==2.10.6
    # via
    #   -r requirements.in // [!code warning]
    #   fastapi
pydantic-core==2.27.2
    # via pydantic
sniffio==1.3.1
    # via anyio
starlette==0.46.1
    # via fastapi
typing-extensions==4.12.2
    # via
    #   fastapi
    #   pydantic
    #   pydantic-core
```

这个等同于 `uv pip compile `，也是 `pip-tools` 中的 `pip-compile` 命令。

***

另外一种比较不常见的方式是使用 `pip freeze` 命令来生成当前环境的依赖列表。

```shell
pip freeze > requirements.txt
```

```text title="requirements.txt"
annotated-types==0.7.0
anyio==4.8.0
fastapi==0.115.11
idna==3.10
pydantic==2.10.6
pydantic-core==2.27.2
sniffio==1.3.1
starlette==0.46.1
typing-extensions==4.12.2
```

豪了，有以上的思路，我们就可以：

:::steps

1. 划分各种依赖环境

   比如项目有 dev | prod | staging 等环境，我们可以将依赖分为不同的文件，例如 `requirements-dev.in`、`requirements-prod.in` 等。

2. 编写这些文件中的特殊依赖

   写好后分别保存到 `requirements-dev.in` 和 `requirements-prod.in` 文件中。

3. 使用 `uv pip compile` 命令编译每个环境的依赖

   ```shell
   uv pip compile requirements-dev.in -o requirements-dev.txt
   # 或者 pip-compile requirements-dev.in -o requirements-dev.txt
   uv pip compile requirements-prod.in -o requirements-prod.txt
   ```

4. 安装依赖

   ```shell
   pip install -r requirements-dev.txt
   pip install -r requirements-prod.txt
   ```

:::

上面讨论的同一个项目中不同开发环境的依赖管理。再扩展下，不同的操作系统也是一样的。

不过 uv 支持跨平台解析，非常方便，比如 `tqdm`

```requirements.in
tqdm
```

两个平台的解析为

:::tabs

@tab ::skill-icons:linux-dark::Linux

```text
tqdm==4.67.1
    # via -r requirements.in
```

@tab ::logos:microsoft-windows-icon::Windows

```text
colorama==0.4.6
# via tqdm

tqdm==4.67.1
    # via -r requirements.in
```

:::

> colorama: Windows 平台下的 tqdm 需要 colorama 包来支持终端颜色输出

这样每个平台间是不兼容的，

如果使用 uv 的 `universal` 选项 ，则会声明 colorama 的平台：

```shell
uv pip compile --universal requirements.in 
```

```text title="requirements.txt"
colorama==0.4.6 ; sys_platform == 'win32' # 这里就声明了
    # via tqdm
tqdm==4.67.1
    # via -r requirements.in
```
