使用 uv 管理 Python 环境

此文由 Mix Space 同步更新至 xLog
为获得最佳浏览体验，建议访问原始链接
https://do1e.cn/posts/citelab/how-to-use-uv

前言

大家都在宣传 uv 的快（UV：Python 包管理神器 - 比 pip 快 100 倍），不过针对我们实验室可能需要在多个机器之间迁移 Python 环境的情况，uv 复现环境的便利性同样也值得关注。

虽然已经在尽力向同门们推荐使用 uv，但大家对 conda 的依赖还是很深，也不愿意尝试新的工具。
因此还是决定写一篇教程，介绍一下 uv 的使用方法，稍微推动一下实验室环境管理的变革好了。

安装 uv

官方文档

Windows 上我还是更推荐使用 winget 安装：

Copy
winget install astral.uv

Linux/macOS 上则可以参考官方文档使用 curl/wget 安装：

Copy
curl -LsSf https://astral.sh/uv/install.sh | sh
# 或者
wget -qO- https://astral.sh/uv/install.sh | sh

初始化 uv 环境

如果如果需要初始化一个新项目，可以在你的 workspace 目录下运行：

Copy
uv init [project-name]
cd [project-name]

或者需要将已有的 Python 项目迁移到 uv 环境下，可以在项目目录下运行：

Copy
cd [project-name]
uv init

此时项目目录中会新建下述文件：

Copy
.git
.gitignore
main.py
pyproject.toml
.python-version
README.md

.git、.gitignore、README.md 就不用解释了

main.py 是一个示例 Python 文件，不需要可以直接删了

pyproject.toml 是 Python 项目的配置文件里面记录了项目的元数据和依赖等信息，内容大致如下：
Copy
[project]
name = "test-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []

.python-version 用于指定当前项目的 Python 版本

指定 Python 版本

编辑 pyproject.toml 文件中的 requires-python 字段，指定当前项目所需的 Python 版本，例如：==3.12、>=3.12 等。

然后运行以下命令安装指定版本的 Python：

Copy
uv python pin 3.12

创建虚拟环境

Copy
uv venv

此命令会在当前项目目录下创建一个虚拟环境，文件夹为 .venv，之后可以参考其输出信息激活虚拟环境，如：

Copy
source .venv/bin/activate
# 或者在 Windows 上
.venv\Scripts\activate

管理项目依赖

基本用法

需要安装新的依赖时，可以使用以下命令：

Copy
uv add [package-name]

实际上就是将之前 pip install 替换为 uv add 即可，但该命令除了将依赖安装到虚拟环境中，还会自动将依赖信息写入 pyproject.toml 文件中的 dependencies 字段，并且更新 uv.lock 锁文件以确保依赖版本的可复现性。uv.lock 文件不需要手动编辑，但是对环境复现相当重要，不要把它放进 .gitignore 中。

比如说安装 requests 后，pyproject.toml 中只会多一条 requests 依赖，而 uv.lock 中则会详细记录 requests 及其所有子依赖的具体版本信息，使用 uv tree 命令可以查看依赖树：

Copy
> uv tree
Resolved 6 packages in 0.75ms
test-project v0.1.0
└── requests v2.32.5
├── certifi v2025.11.12
├── charset-normalizer v3.4.4
├── idna v3.11
└── urllib3 v2.6.1

如果不需要某个依赖了，可以使用以下命令将其卸载：

Copy
uv remove [package-name]

尽可能严格使用 uv 命令管理环境，虽然也提供了 uv pip 命令来兼容 pip 的用法，但该命令不会更新 pyproject.toml 和 uv.lock 文件，将导致环境不可复现的问题。

镜像源

如果需要换 Pypi 镜像源，可在 pyproject.toml 文件中添加如下内容（以南大镜像源为例）：

Copy
[[tool.uv.index]]
name = "nju-mirror"
url = "https://mirror.nju.edu.cn/pypi/web/simple"
default = true

pip 中的 index-url

部分包会在自己的 PyPI 镜像源上发布，比如 torch 的 pip 安装命令（参见 torch 官方文档）如下：

Copy
pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu130

需要参考 Using uv with PyTorch 将下述内容加入 pyproject.toml 文件中：

Copy
[[tool.uv.index]]
name = "pytorch-cu130"
url = "https://download.pytorch.org/whl/cu130"
explicit = true

[tool.uv.sources]
torch = [
{ index = "pytorch-cu130", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
]
torchvision = [
{ index = "pytorch-cu130", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
]

否则将不会安装 CUDA 版本的 torch 和 torchvision，而是安装 CPU 版本。

这里首先定义了一个名为 pytorch-cu130 的索引源，url 指定为上述 torch 文档中给出的 --index-url，不过更推荐使用南大镜像源的 https://mirror.nju.edu.cn/pytorch/whl/cu130。
然后在 tool.uv.sources 中指定了 torch 和 torchvision 这两个包需要从该索引源安装（注：marker 指定了仅当操作系统是 Linux 或 Windows 时，才使用 pytorch-cu130 索引。对于其他操作系统，如 macOS，此配置会被忽略，依然使用默认源）。

复现环境

当你需要在另一台机器上复现当前项目的 Python 环境时，只需克隆项目代码后，运行以下命令：

Copy
uv sync

如果想要实现在另一个项目中复现相同的环境，则可以复制 pyproject.toml、uv.lock、.python-version 文件到目标项目目录下，更新 pyproject.toml 中的项目信息，然后运行同样的命令：

Copy
uv sync

构建和发布包

如果你是在写一个可供其他人安装的 Pypi 包，你还需要考虑哪些依赖是开发时依赖，哪些是运行时依赖，开发依赖需要使用 uv add --dev [package-name] 命令安装。

之后还需要补充 pyproject.toml 文件中的项目信息，比如作者、许可证，以及构建信息等，考虑到搞科研的大部分时候并不会发布包，这里就不展开讲了，可以参考下述内容进行理解：

Copy
[project]
name = "NJUlogin"
version = "3.6.1"
description = "The Nanjing University login module, which can be used to login to the various campus web sites"
authors = [{ name = "Do1e", email = "i@do1e.cn" }]
readme = "README.md"
requires-python = ">=3.10,<4.0"
dependencies = [
"requests>=2.32.0",
"pillow>=11.0.0",
"numpy>=2.0.0",
"lxml>=5.3.0",
"pycryptodome>=3.21.0",
"onnxruntime>=1.20.0",
"cryptography>=43.0.0",
]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]

[project.scripts]
NJUlogin = "NJUlogin.__main__:main"

[tool.setuptools.packages.find]
include = ["NJUlogin", "NJUlogin.*"]

[project.urls]
Homepage = "https://github.com/Do1e/NJUlogin"
Repository = "https://github.com/Do1e/NJUlogin"

[dependency-groups]
dev = [
"pre-commit>=4.3.0",
"ruff>=0.14.6",
]

[[tool.uv.index]]
url = "https://mirror.nju.edu.cn/pypi/web/simple"
publish-url = "https://upload.pypi.org/legacy/"
default = true

[tool.ruff]
line-length = 100

[tool.ruff.lint]
ignore = ["C901", "E501", "E721", "E741", "F402", "F823"]
select = ["C", "E", "F", "I", "W"]

[tool.ruff.lint.isort]
lines-after-imports = 2

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.uv]
package = true

之后即可，使用以下命令构建和发布包：

Copy
uv build
uv publish

其他实用命令

uv cache clear：清除 uv 缓存，释放其占用的磁盘空间

uv tree：查看当前项目的依赖树

uv run main.py：等价于激活虚拟环境后运行 python main.py，可以用来运行项目中的 Python 脚本

uvx [script-name]：运行工具，有些 Python 包会提供命令行工具，可以使用 uvx 命令运行它们，例如 uvx NJUlogin -h。