预计阅读时间:18 分钟
用过 Python 的人多少都被环境问题折磨过:pip 装包、venv 建虚拟环境、requirements.txt 管依赖,还得手动折腾 Python 版本。项目还没开始写,环境先折腾半天。
uv 是 Astral 公司(就是做 Ruff 的那家)用 Rust 写的 Python 包与项目管理工具,目标是把上面这些环节合并成一条命令。官方宣传语是 "An extremely fast Python package and project manager",官方基准中依赖安装速度通常可达到 pip 的 10~100 倍(具体提升取决于缓存状态和依赖规模)。更重要的是,uv 自己管理 Python 版本——电脑上没装 Python 也能直接开工。
本文基于 uv 0.12.1,覆盖安装、常用命令、日常开发、正式项目、部署和关键文件解读。
安装
Windows
打开 PowerShell,执行一行命令:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
uv 会被安装到 %USERPROFILE%\.local\bin\uv.exe。安装脚本会自动写入 PATH;如果新开终端仍找不到 uv 命令,把 %USERPROFILE%\.local\bin 手动加进系统环境变量即可。
也可以用包管理器:
winget install --id astral-sh.uv -e
# 或
scoop install main/uv
Linux
在终端执行官方安装脚本:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv 被安装到 ~/.local/bin/uv。如果该目录不在 PATH 里,手动加一下:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
部分发行版或第三方仓库可能提供 uv,但版本通常不如官方安装脚本及时,使用前建议确认具体版本。
验证安装
uv --version
输出版本号即安装成功。uv 的核心项目管理命令在 Windows、Linux 和 macOS 上基本一致,主要区别在于路径格式、Shell 引号和虚拟环境激活命令。以 Windows 和 Linux 为例:
| Windows | Linux | |
|---|---|---|
| 官方脚本默认安装位置 | %USERPROFILE%\.local\bin\uv.exe |
~/.local/bin/uv |
| 项目虚拟环境(.venv) | .venv\Scripts\python.exe |
.venv/bin/python |
常用命令一览
| 命令 | 作用 |
|---|---|
uv init |
新建项目 |
uv add 包名 |
添加依赖并安装 |
uv remove 包名 |
移除依赖 |
uv sync |
同步项目环境,并按需创建或更新 uv.lock |
uv run 命令 |
在项目虚拟环境中执行命令 |
uv lock |
生成或更新 uv.lock |
uv lock --upgrade |
升级到约束允许的最新兼容版本 |
uv python install 版本 |
安装指定版本 Python |
uv python pin 版本 |
固定项目使用的 Python 版本 |
uv tree |
查看依赖树;加 --outdated 可显示有新版的可升级依赖 |
uv build |
构建发行包(sdist + wheel) |
uv publish |
发布到 PyPI |
uvx 工具名 |
临时运行 CLI 工具,不全局安装 |
uv tool install 工具名 |
全局安装 CLI 工具(类似 pipx) |
uv cache clean |
清理下载缓存 |
下面按三个场景展开。
日常开发:个人项目怎么用
从零开始
uv init my-project
cd my-project
从 uv 0.12 开始,uv init 默认创建一个采用 src/ 布局的可打包应用:使用 uv_build 构建后端,并在 pyproject.toml 里声明命令行入口(my-project = "my_project:main"),同时生成 .gitignore、.python-version,并顺手 git init。想创建旧式的平铺脚本项目(根目录下 main.py),用 uv init --no-package。
添加依赖:
uv add requests # 运行时依赖
uv add --dev pytest # 开发依赖(测试、lint 之类)
uv add 会自动创建 .venv 虚拟环境并安装依赖,同时更新 pyproject.toml 和 uv.lock,全程不用手动激活环境。
写代码、跑程序,一律用 uv run:
uv run my-project # 0.12 默认模板自带命令行入口
uv run pytest # 跑测试
uv run python -c "import requests; print(requests.__version__)"
(如果用了 --no-package 平铺布局,uv run main.py 一样可以。)
uv run 会自动找到项目根目录的 .venv 并执行命令,不需要激活虚拟环境。想用传统方式激活也可以:
- Windows PowerShell:
.venv\Scripts\Activate.ps1 - Linux/macOS:
source .venv/bin/activate
换机器、clone 别人的项目
拿到项目后只需要:
git clone <仓库地址> && cd 项目目录
uv sync
uv sync 根据 uv.lock 还原项目的默认开发环境(可选依赖组不会默认安装,需要时用 uv sync --extra web 或 --all-extras);如果项目要求的 Python 版本本机没有,uv 会自动下载,什么都不用装。
升级依赖
uv tree --outdated # 查看可用的新版本
uv lock --upgrade # 升级到约束允许的最新兼容版本
uv lock --upgrade-package requests # 只升级指定依赖
单文件脚本也可以
不想建项目、只想跑个一次性脚本?uv 支持 PEP 723,在文件头部声明依赖即可:
# /// script
# dependencies = ["requests"]
# ///
import requests
print(requests.get("https://example.com").status_code)
uv run script.py
uv 会按需创建并管理隔离环境,自动安装 requests 再运行,无需手动建环境或激活;相关下载和构建数据会被缓存以加速后续运行,需要时可用 uv cache clean 清理。
写正式项目:库或应用怎么用
库项目用 --lib
个人脚本用默认(或 --no-package)布局就够;开发要发布的库,用 --lib:
uv init --lib --python 3.12 my-lib
--python 3.12 会以 Python 3.12 初始化项目:设置相应的 requires-python 并生成 .python-version。--lib 生成适合库的 src/ 布局,并包含 py.typed 等库模板内容。依赖照常用 uv add 添加。注意 uv python pin 3.12 只修改 .python-version,不会改动 requires-python;调整最低兼容版本时需要一并检查 pyproject.toml。
可选依赖(extras)
uv add --optional web "fastapi[standard]"
uv add --optional test pytest
用户安装时可以按需选择:pip install my-lib[web] 或 uv add my-lib[web]。
构建与发布
uv build # 在 dist/ 生成 .whl 和 .tar.gz
uv publish # 发布到 PyPI,需先配置 token
uv publish 通过 UV_PUBLISH_TOKEN 环境变量或 --token 参数传入 PyPI Token;在 GitHub Actions、GitLab CI 等环境中也可以使用 Trusted Publishing 免 Token 发布。发布前建议先 uv build 试构建一遍。
多项目 workspace
一个仓库里放多个关联项目(比如 SDK 和它的 demo),在根 pyproject.toml 声明:
[tool.uv.workspace]
members = ["sdk", "demo"]
workspace 中的项目共享一个 uv.lock 和项目环境,uv 会统一解析各成员的依赖。成员之间需要互相引用时,在 dependencies 中声明包名,并通过 [tool.uv.sources] 将其指定为 workspace 成员(sdk = { workspace = true })。这是进阶功能,用到时再查文档即可。
部署:服务器与 Docker
服务器直接部署
git clone <仓库地址>
cd 项目目录
uv sync --locked --no-dev # 校验 lock,并排除默认的 dev 依赖组
两个关键 flag:
--locked:要求uv.lock已存在且与项目声明一致,不一致直接报错,并禁止部署时重新解析或改写锁文件--no-dev:排除默认的 dev 依赖组(--no-group dev的别名,pytest、ruff 这类依赖都在这里)。如果项目配置了其他默认依赖组,它们仍会被安装;要排除所有默认组,用--no-default-groups
--frozen 与之类似但更宽松:直接使用现有 lock,跳过一致性检查。它适合明确需要跳过检查的场景,比如 Docker 分层构建时只拷入了部分项目文件。
启动服务可以用 uv run,或直接指向 .venv 里的可执行文件(systemd、supervisor 等进程管理器更喜欢这种方式):
uv run --locked --no-dev gunicorn app:app
# 或
ExecStart=/srv/app/.venv/bin/gunicorn app:app
Docker 部署
以下以 FastAPI 项目为例:假设项目已经通过 uv add "fastapi[standard]" 安装依赖,并在 app/main.py 中定义了名为 app 的 ASGI 应用。
推荐的写法:从 uv 官方镜像拷二进制进容器(固定 uv 版本,与本地开发环境保持一致,避免镜像里的 uv 升级引起行为漂移;要求更高可进一步固定镜像 digest),依赖声明和源码分开 COPY 以利用层缓存:
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:0.12.1 /uv /uvx /bin/
WORKDIR /app
# 先只拷依赖声明,安装依赖(这层只有改依赖时才会重建)
COPY pyproject.toml uv.lock ./
RUN uv sync --locked --no-dev --no-install-project
# 再拷源码,安装项目本身
COPY . .
RUN uv sync --locked --no-dev
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
几个要点:
- 依赖层和源码层分开:只改代码不触发依赖层重建,构建速度快很多
- 第一次
uv sync用--no-install-project:源码还没拷进来,只装依赖 - 两层都能用
--locked:每层都同时拷入了pyproject.toml和uv.lock,一致性检查能通过;只有 workspace 这类声明文件拷不全的场景才需要--frozen - 单项目容器中,也可以把
UV_PROJECT_ENVIRONMENT设为/usr/local,直接装进系统 Python 前缀,PATH 都不用改。不要在多个项目共享的 Python 环境里用这种方式——uv sync 的精确同步可能移除其他项目安装的包 - 别忘了在
.dockerignore里排除.venv,否则本机虚拟环境会被打进镜像
这些文件都是什么
执行 uv init,再运行一次 uv add、uv sync 或 uv run 后,一个完整的项目通常包含这些文件(.venv 和 uv.lock 都是首次同步时才生成,uv init 本身只创建其余部分):
my-project/
├── .venv/ # 首次同步时生成的虚拟环境,不要提交
├── .gitignore # git 忽略规则
├── .python-version # 项目使用的 Python 版本
├── README.md # 项目说明
├── pyproject.toml # 项目配置与依赖声明
├── src/
│ └── my_project/ # 项目源码
└── uv.lock # 首次锁定或同步时生成
0.12 默认模板没有根目录 main.py:命令行入口声明在 pyproject.toml 的 [project.scripts],实现放在 src/my_project/ 的 main();旧式平铺布局的 main.py 只在 --no-package 时生成。
pyproject.toml
项目的"身份证"加"采购单":项目名、版本、Python 版本要求、依赖列表,以及可选的构建系统和工具配置(ruff、mypy 的配置也写在这里)。pyproject.toml 是现代 Python 项目的统一配置文件,其中 [project] 表遵循 PEP 621,用于声明项目名称、版本、Python 版本要求和运行依赖等元数据。它可以取代过去写在 setup.py 或 setup.cfg 中的大部分配置;在 uv 管理的新项目中,依赖通常也直接声明在这里,无需再手动维护 requirements.txt,但后者仍是有效的依赖格式:
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["requests>=2.32"]
依赖一般由 uv add 帮你维护,很少需要手写。0.12 默认模板还会在 [project.scripts] 里声明 my-project = "my_project:main",这就是 uv run my-project 能直接运行的来源。
uv.lock
锁文件,记录所有依赖(包括间接依赖)的精确版本、哈希和来源。uv.lock 是跨平台锁文件,同一份文件可以同时记录不同操作系统、架构和 Python 版本对应的解析结果,使不同机器在各自平台条件下使用一致、可重复的依赖版本——"在我这能跑,到你那就报错"的解药。必须提交到 git,可以类比前端的 package-lock.json。
.python-version
通常包含一行 Python 版本请求(如 3.12,表示 3.12 版本系列,不保证所有人为完全相同的补丁版本),让 uv 为项目选择同一 Python 版本系列;uv 也支持比单纯版本号更复杂的版本请求。由 uv python pin 3.12 或 uv init --python 3.12 生成;同步时如果本机没有这个版本,uv 会自动下载。建议提交到 git,让团队成员使用同一版本系列。
.gitignore
git 的忽略清单。它不是 uv 发明的,但 uv init 会自动生成一份,包含 __pycache__/、dist/、.venv 等常见条目。.venv 一定不能入库——它是本机生成、动辄几百 MB 的虚拟环境,别人可以通过 uv sync 一键重建。
.venv
uv 在项目根目录创建的虚拟环境。Windows 下解释器在 .venv\Scripts\python.exe,Linux 下在 .venv/bin/python。平时不用管它,uv run 会自己找;想删就 rm -rf .venv 然后重新 uv sync。
实用小技巧
uv 当 pip 用:老项目还在用 requirements.txt?uv venv 之后 uv pip install -r requirements.txt 兼容绝大多数常见 pip 工作流(但不是 pip 的完整复刻)。新项目建议直接用 uv 的项目管理,一条命令同时维护 pyproject 和 lock。
uvx 临时工具:
uvx ruff check . # 临时跑一次 ruff,不污染环境
uvx black . # 临时运行,不全局安装
uv tool 全局工具(相当于 pipx):
uv tool install ruff
uv tool upgrade --all
国内镜像加速。PyPI 走清华源,Python 本体下载走南京大学镜像(如果安装某些较旧的 Python 版本遇到 404,可以切换到 npmmirror 的 https://registry.npmmirror.com/-/binary/python-build-standalone/ 等其他镜像,或暂时恢复官方源):
export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple
export UV_PYTHON_INSTALL_MIRROR=https://mirror.nju.edu.cn/github-release/astral-sh/python-build-standalone/
缓存:uv 把下载过的包缓存到本地(Linux 在 ~/.cache/uv,Windows 在 %LOCALAPPDATA%\uv\cache),删掉 .venv 或切换项目时,已有缓存通常可以复用,减少重复下载。磁盘紧张时 uv cache clean 清空。
总结
uv 把 Python 开发里最繁琐的环境与依赖管理,压缩成了几条命令。记住最小集就够了:
uv init # 新建项目
uv add 包名 # 加依赖
uv run 命令 # 跑代码,不用激活环境
uv sync # 别人拿到项目后一键还原
官方文档:https://docs.astral.sh/uv/
本文由 Wuqi 原创,转载请注明出处。