聊到 Python 开发和 Windows 环境,最近一年多我几乎逢人就会推荐 uv 这个工具。它既不是老牌 pip 的“美化皮肤”,也不是又一个环境管理小玩具。作为一个用 Rust 重写、目标很明确要替代 pip、virtualenv、poetry 这一整条链路的新一代包管理器,uv 在“速度”和“一致性”这两件事上给我带来的体验,是近几年里最明显的一次工具升级。尤其是经常在 Windows 上切项目、装依赖、折腾 Python 解释器版本的人,用 uv 之后基本回不去了。
这篇文章就围绕一个问题展开:uv 到底是什么,Windows 上怎么装,以及日常最常用的那几条命令怎么用。我不会把官方文档抄一遍,而是按我自己踩过的坑和实际工作流来讲。无论你是刚入门 Python 的小白,还是被依赖地狱折磨过的老手,下面这些内容都能直接拿来用。
1. uv 到底是什么?先搞清楚它的定位
1.1 uv 的定位:不是“pip 加速器”,而是 Python 项目工具链的重写
uv 是 Astral 公司出的开源工具,和近几年爆火的 Python 代码检查器 Ruff 是同一个团队。它用 Rust 编写,官方对自己的定位是“一个极其快速的 Python 包和项目管理器”。很多人第一次听说 uv,是因为它在安装依赖时的速度对比图很夸张,几十个包下来居然能快到一秒内完成,于是下意识把它归为“pip 加速器”。
这个理解不能说完全错,但格局小了。uv 不只是把 pip 下载包的过程变快,它实际上把 Python 项目开发里一大串常用工具的活都干了:创建虚拟环境、安装包、解析版本依赖、锁定依赖、管理 Python 解释器版本、运行脚本、甚至构建和发布项目。简单说,过去你需要先装 Python 解释器,再装 pip,再手动跑 venv 建环境,再用 pip 一个个装包,再用 pip-tools 或 poetry 处理依赖锁定,现在这些步骤在 uv 里基本被压缩成了几条命令。
它适合谁?第一类是写脚本和做数据处理的开发者,想省掉“建环境-激活-装依赖”的重复劳动;第二类是维护多个 Python 项目的同学,每个项目用不同 Python 版本,切换环境很痛苦;第三类是纯粹受不了 pip 在国内网络环境下反复超时、下载太慢的人。当然,如果你只想在 Windows 上把 uv 当 pip 用,它也完全支持,门槛比想象中低很多。
1.2 为什么快:Rust 重写带来的几个物理层面优势
uv 快的核心原因,不是单纯“代码优化得好”,而是它在几个底层环节上都做了和传统工具不一样的设计。
第一个层面是启动速度。pip 本身是一个 Python 工具,每次执行都要先启动一个 Python 解释器,加载一堆模块,整个过程通常要几百毫秒到几秒。uv 是编译好的 Rust 二进制,在 Windows 上就是一个 uv.exe,启动开销很小,经常是毫秒级别。你交互式地反复执行命令时,这个差别会非常明显。
第二个层面是并发下载与全局缓存。pip 默认串行下载包,而 uv 会使用并发请求,并且把所有下载过的包存放在一个全局缓存目录中。下次无论哪个项目用到同一个包、同一个版本,直接通过硬链接从缓存复制,不需要重新下载。速度提升在这个环节是最直观的。
第三个层面是依赖解析算法。解析“A 依赖 B,B 依赖 C,C 又限制 A 的版本”这类复杂关系时,pip 会用回溯搜索空间,在某些场景下会非常慢甚至卡死;uv 采用更高效的解析器,很多场景下能在几秒内给出结果。同时 uv 会把解析结果写进 uv.lock 锁文件,之后安装直接按锁文件装,不会再反复猜测。
当然,干净利落地“快”也是有代价的:uv 与某些非常小众或者老旧的包兼容性偶尔会有问题,但就我实际使用看下来,日常 Web 开发、数据处理、机器学习相关的包都跑得很稳。
1.3 uv 到底能替代哪些原有工具
我整理了一个对照表,方便你快速理解它在项目里扮演的角色:
| 原有工具链 | 场景 | uv 对应方案 |
|---|---|---|
| pip install | 安装 Python 包 | uv pip install |
| venv / virtualenv | 创建虚拟环境 | uv venv |
| pip-tools | 锁定依赖版本 | uv lock |
| poetry 部分功能 | 项目依赖管理 | uv add / uv remove |
| pyenv 的部分功能 | 管理多个 Python 版本 | uv python install / pin |
| pipx | 安装命令行工具 | uv tool install |
这张表不是说 uv 能 100% 等价替代所有工具,而是说它覆盖了绝大多数个人开发者的日常场景。像 poetry 还有完整的构建发布流程,uv 也通过 uv build 和 uv publish 提供了对应方案,只是我平时用得不多,后续你可以自己官方文档继续研究。
对于一个普通项目,以前你从创建虚拟环境到装完依赖可能要跑四五条命令,现在uv init、uv add flask、uv run python main.py三条命令基本就走完了。这就是它最具冲击力的地方。
2. Windows 上安装 uv 的几种方式与关键细节
2.1 官方 PowerShell 安装脚本(推荐)
Windows 上最推荐的方式,是使用官方 PowerShell 安装脚本。按 Win + X 打开终端,输入下面这行:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"这里简单解释一下每个部分在干什么:irm是Invoke-RestMethod的缩写,用于下载远端脚本;iex是Invoke-Expression,表示执行下载下来的内容;前面的-ExecutionPolicy ByPass -c则让 PowerShell 临时绕过执行策略限制,避免因为系统默认不允许运行脚本而报错。
装完之后,uv 会被放到%USERPROFILE%\.local\bin目录下,也就是你的用户目录里。通常安装脚本会自动帮你把该目录加入 PATH 环境变量,但由于 Windows 对系统环境变量的刷新有延迟,已打开的终端可能找不到 uv,你需要新开一个终端窗口再试。
2.2 pip 安装、winget 安装等方式
如果你不想用官方脚本,还至少有三种方式可选。
方式一:通过 pip 安装,这个对 Python 用户最熟悉:
pip install uv这种方式会把 uv 装成一个普通 Python 包,可执行文件位于你当前 Python 环境的 Scripts 目录。它的好处是迁移成本为零,缺点是这个 uv 本身是作为 Python 包分发的,启动时需要经过一层包装,性能会略受影响,但日常使用感觉不明显。
方式二:通过 winget。Windows 11 和 Windows 10 新版本自带 winget,直接执行:
winget install --id=astral-sh.uv -e方式三:如果你装了 Scoop 或 Chocolatey,也可以用scoop install uv或choco install uv。我个人在 Windows 上的习惯是优先用官方脚本,因为升级体验最好,它能直接在原目录替换版本,uv self update也与这种方式配套有效。
2.3 安装后先做三件事:验证、配置 PATH、准备缓存目录
无论用哪种方式,装完第一步都是验证版本:
uv --version如果提示“不是内部或外部命令”,不要慌,大概率是 PATH 没生效。检查系统环境变量,看有没有%USERPROFILE%\.local\bin,没有就手动加,然后重开终端。用 pip 安装的话,确认 Scripts 目录也加到 PATH 里了。
第二步是了解一下 uv 默认会把数据放在哪里。这是很多 Windows 用户困惑的地方。默认情况下:
- 依赖缓存目录:
C:\Users\你的用户名\AppData\Local\uv\cache - Python 解释器安装目录:
C:\Users\你的用户名\AppData\Roaming\uv\python - 命令行工具安装目录:
C:\Users\你的用户名\AppData\Roaming\uv\tools
你可以通过环境变量UV_CACHE_DIR、UV_PYTHON_INSTALL_DIR等来修改这些目录。如果 C 盘空间紧张,我强烈建议把缓存目录设置到其他磁盘,比如:
setx UV_CACHE_DIR "D:\uv-cache"这里用setx会写入用户级环境变量,重开终端后生效。
2.4 配置国内镜像源,解决下载慢和超时
“uv 国内源”是我看到搜索词里频率很高的一条。uv 默认从官方 PyPI 下载包,国内网络环境不稳定时,下载包或解析索引很容易卡住。解决方案是配置国内镜像源。
最常用的是清华源和阿里源,在环境变量层面设置:
setx UV_INDEX_URL "https://pypi.tuna.tsinghua.edu.cn/simple"也可以设置UV_DEFAULT_INDEX,它用于指定“默认索引”,在解析某些非 Python 包来源时很有用;而UV_INDEX_URL更像传统意义上的 pip 参数,直接指定主源。如果你只是想把 PyPI 替换成国内镜像,只设置UV_INDEX_URL就够了。
更灵活的做法是在项目pyproject.toml里配置:
[tool.uv] index-url = "https://mirrors.aliyun.com/pypi/simple/"这样只对该项目生效,不污染全局。临时命令式也可以:
uv pip install --index-url https://pypi.tuna.tsinghua.edu.cn/simple flask这里还要提醒一句:如果你已经设置了UV_INDEX_URL,后来又装回官方源,记得把环境变量删掉或者改成https://pypi.org/simple,否则你可能一直以为某个包不存在,其实是镜像源同步有延迟。
3. uv 的基础用法:日常高频命令
3.1 uv venv:干净利落地创建虚拟环境
创建虚拟环境是 Python 项目的第一步。uv 的命令非常简单:
uv venv默认会在当前目录创建一个.venv文件夹。如果你想指定名字:
uv venv myenv也可以直接指定 Python 版本创建环境:
uv venv --python 3.11这个命令最大的好处是:如果本机没有 3.11 的 Python,uv 会自动下载一个由它管理的 Python 解释器,而不是报错让你去官网安装。在 Windows 上创建出的环境,结构默认是.venv\Scripts\python.exe,这正是 Windows 虚拟环境的标准布局。
创建完之后,如果你习惯传统方式激活环境,在 PowerShell 里运行:
.venv\Scripts\Activate.ps1但 uv 最优雅的用法其实不需要手动激活,后面讲uv run时你就明白了。
3.2 uv pip install:像 pip 一样但更快
uv 提供了uv pip子命令,基本完全兼容 pip 的常用参数。它面向的是“已经习惯了 pip 工作流”的场景,比如你手里有一个老项目,只有 requirements.txt,没有 pyproject.toml,那就可以这样:
uv pip install -r requirements.txt这个命令会往当前激活的虚拟环境里安装依赖。如果没有激活环境,uv 会默认查找当前目录下的.venv;如果两者都不存在,uv 会提示你先创建虚拟环境。
单独安装包也很直观:
uv pip install flask requests常用参数基本照搬 pip,比如--index-url、--no-cache、--upgrade等。可以说只要你曾经会写 pip 命令,那你就会用 uv pip,学习成本几乎为零。这也是 uv 故意保留这层兼容接口的原因:让 pip 用户可以先感受到速度,再慢慢迁移到更完整的项目级工作流。
3.3 uv add / uv remove / uv sync:项目级依赖管理
更完整也更推荐的工作流,是直接让 uv 管理项目依赖。在项目目录中执行:
uv init它会生成一个pyproject.toml文件,外加一个示例main.py。然后添加依赖:
uv add flask这条命令会做一串事情:读取 pyproject.toml、解析依赖、自动创建.venv(如果还没有)、安装 flask、把依赖写入 pyproject.toml,并生成或更新uv.lock锁文件。这也是 uv 和纯 pip 最大的区别:项目依赖有了声明文件和锁文件,其他人克隆项目后只需一条命令就能还原环境。
删除依赖用:
uv remove flask如果你手动修改了 pyproject.toml,或者从 Git 克隆了一个 uv 项目,需要把依赖同步到本地环境:
uv syncuv sync会读取 pyproject.toml 和 uv.lock,创建虚拟环境并安装所有依赖。我发现很多 Windows 用户第一次用uv sync时什么都没发生,误以为命令没生效,其实是因为 uv 发现环境已经和锁文件一致,所以“什么都不做”就是正确的表现。
如果你需要区分开发依赖和生产依赖,可以:
uv add --dev pytest这样 pytest 会进入[dependency-groups]分类,生产安装时可以跳过。
3.4 uv run:一条命令跑脚本和项目
uv run是我个人认为最惊艳的功能。它的核心逻辑是:在项目环境下自动运行命令,不需要你手动激活虚拟环境。
在刚才那个初始化好的项目里,直接执行:
uv run python main.pyuv 会判断当前目录下的.venv是否存在,不存在就根据 pyproject.toml 自动创建,然后运行。这彻底解决了 Windows 上“忘记激活虚拟环境”导致用到全局 Python 的尴尬。同样,你想跑测试:
uv run pytest如果 pytest 已经声明在项目依赖里,它会直接被调用。
更夸张的用法是跑独立脚本。uv 支持 PEP 723,允许在 Python 文件顶部以内联注释声明依赖:
# /// script # requires-python = ">=3.11" # dependencies = ["flask", "requests"] # /// from flask import Flask app = Flask(__name__) @app.route("/") def index(): return {"hello": "uv"} app.run()然后直接:
uv run app.pyuv 会看到脚本里的依赖声明,临时创建一个环境并安装,然后执行脚本。这对分发小工具、写一次性脚本来说非常爽,别人拿过来直接uv run就能跑,不用再写 README 里那一长串安装教程。
3.5 uv python:按需安装和切换 Python 版本
很多人搜“uv 切换环境”,其实就是想要一套靠谱的多 Python 版本管理方案。uv 内置了 Python 版本管理功能,查看能装什么版本:
uv python list安装指定版本:
uv python install 3.11 3.12查看某个版本的解释器路径:
uv python find 3.12在特定项目里固定一个 Python 版本:
uv python pin 3.12这会往项目里写入.python-version文件,之后这个项目下的uv run、uv sync都会优先使用 3.12 版本。手动创建环境时也可以明确指定:
uv venv --python 3.12 myenv在 Windows 上这个功能尤其救急:以前你需要在官网下载不同 Python 版本,还要自己管理 PATH、处理 py launcher 的优先级,现在 uv 把解释器安装到了它自己的目录里,不污染系统,不同项目之间随意切换。再也不用担心“我明明装了 3.12,但 python 命令还是旧的 3.10”。
3.6 uv lock 与 uv tree:锁定依赖和梳理依赖树
当项目依赖比较复杂时,锁定版本就变得很重要。uv 会在执行uv add时自动更新uv.lock,但如果你想单独刷新锁文件而不安装环境:
uv lock这个命令会做一次完整的依赖解析,把所有传递依赖的版本都固定住。别人拿到项目后执行的uv sync,也完全是按锁文件来的,不会因为某个依赖发布了新版本而导致环境不一致。
想要查看依赖关系树:
uv tree输出类似:
demo-project v0.1.0 ├── flask v3.0.0 │ ├── click v8.1.7 │ └── jinja2 v3.1.3 └── requests v2.31.0 └── urllib3 v2.1.0这在排查“哪个包带了多余依赖”“哪个传递依赖版本冲突”时非常好用。遇到问题不用再跑去 PyPI 页面手动查依赖图,一条命令解决。
4. 实战:用 uv 把一个小项目完整跑起来
4.1 项目场景
为了把上面这些命令串起来,我准备一个小场景:创建一个 Flask 服务,提供一个接口返回 JSON 数据,然后启动服务并验证访问结果。整个流程从零开始,模拟你在 Windows 上接到一个新需求的场景。
假设你想把项目放在D:\projects\uv-demo。先创建一个目录并进入:
mkdir D:\projects\uv-demo cd D:\projects\uv-demo4.2 uv init 初始化项目
在当前目录执行:
uv init --app--app参数表示创建一个应用项目模板,它会生成 pyproject.toml、main.py、README.md 等文件。如果你不想要示例代码,可以用--bare,这样只生成 pyproject.toml。
看一下生成的pyproject.toml,大概长这样:
[project] name = "uv-demo" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = []这里requires-python会根据你当时默认的 Python 版本生成。如果你希望项目支持 3.11,可以修改这里的值,或者提前用uv python pin 3.11。
继续添加依赖:
uv add flask你会在终端看到 uv 自动创建.venv、解析依赖、安装包的过程。等它跑完后,pyproject.toml 里已经多了一行flask,同时目录下多了uv.lock。整个过程通常几秒钟完成。
4.3 添加依赖、启动服务、验证
因为main.py默认只是个打印函数,我们需要自己写一个 Flask 应用。直接新建一个app.py:
from flask import Flask app = Flask(__name__) @app.route("/") def index(): return {"message": "uv demo", "status": "ok"} if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True)然后运行:
uv run python app.py正常启动后,浏览器访问 http://127.0.0.1:5000 ,就能看到 JSON 响应。整个过程里我没有手动激活虚拟环境,也没有去 pip 安装任何东西。这就是 uv 项目工作流的完整闭环:初始化、加依赖、运行。
如果要临时跑一段验证代码,不需要写文件:
uv run python -c "import flask; print(flask.__version__)"这个命令会直接在项目环境里执行,完全不会污染全局 Python。
如果你只想快速起步,不想维护项目文件夹结构,那也可以用脚本方式:在任意目录写一个带 PEP 723 注释的脚本文件,然后uv run script.py。两种思路按项目需求选择。
4.4 uv 与 PyCharm / VS Code 配合使用
在 Windows 上使用 PyCharm 的朋友,经常会在意 uv 创建的虚拟环境能不能被 IDE 识别。答案是可以的,不用安装任何额外插件。
在 PyCharm 中打开项目后,进入 Settings -> Project -> Python Interpreter,点击右边的“Add Interpreter”,选择“Local Interpreter”,再选“Existing”。然后在解释器路径里,找到项目目录下的.venv\Scripts\python.exe。保存后,PyCharm 会读取这个环境里的包,代码补全、调试、运行都可以正常走。
有一个细节需要注意:PyCharm 自己提供的“创建虚拟环境”功能,和 uv 是两套逻辑。如果你已经用 uv 建好了.venv,就不要再用 PyCharm 的界面创建新环境,否则容易在切换解释器时出现混乱。正确做法是让 PyCharm 直接使用 uv 生成的环境。
VS Code 用户更简单:打开项目后,按 Ctrl+Shift+P,输入“Python: Select Interpreter”,选择“Enter interpreter path”,再选择.venv\Scripts\python.exe。也可以在 VS Code 终端里直接执行uv run python app.py,它会自动识别项目环境,其实不手动选择解释器也能跑。
5. 常见问题与避坑指南(Windows 特供)
5.1 C盘 AppData/Local/uv 越来越大怎么办?
这个问题的搜索词是“c盘里users appdata local uv”,说明很多 Windows 用户发现了这个目录的膨胀。它主要存放 uv 的全局缓存,包括所有下载过的包和编译产物。缓存不是垃圾,它能让你在下一个项目里秒装依赖,所以不建议频繁删光,但确实可以把缓存迁移到别的盘。
思路是设置环境变量UV_CACHE_DIR指向 D 盘或其他空间充足的目录。已经产生的大缓存,可以用如下命令清理:
uv cache cleanuv cache clean会清空整个缓存,下次下载所有包会重新来。另一个更温和的命令是:
uv cache prune它只会清理过期的、不再被任何项目引用的缓存条目,不会把热门的包删掉。如果你用的是 uv 管理的 Python 解释器,它们存在%APPDATA%\uv\python,这部分也会占几个 GB,可以通过UV_PYTHON_INSTALL_DIR指定到其他目录。
5.2 报错“error: start the windows daemon from a non-elevated terminal; shared clients”怎么处理
这个报错是 Windows 环境下 uv 特有的。我在开发群里见过好几次,也实际踩过一次。它通常和 uv 在 Windows 上使用的一个后台进程机制有关,当你的终端窗口权限不一致,或者 uv 版本较老,就可能触发类似“从非提权终端启动 Windows daemon; shared clients”的错误提示。
处理顺序很简单:
- 先把 uv 升级到最新版,执行
uv self update。多数这类问题在后续版本中已经修复。 - 保持终端权限一致。比如你一直用普通的 PowerShell 窗口,就别切到管理员窗口和普通窗口混用,后台进程被权限隔离后容易误判。
- 如果还报错,可以临时禁用这个后台 daemon 机制,设置环境变量:
setx UV_DISABLE_DAEMON "1"设置完重启终端再试。如果问题消失,说明是后台共享机制和你的系统环境冲突,禁用掉并不会影响 uv 的核心功能,只是可能损失一部分启动速度优化。
注意:使用 setx 设置环境变量后,需要重新打开终端才能生效。临时生效则可以在当前 PowerShell 里用$env:UV_DISABLE_DAEMON="1"。
5.3 uv 和 PyCharm 的虚拟环境到底怎么选?
这个问题我前面在 4.4 节里讲了一个操作路径,这里再补几个容易踩坑的点。
首先,不要在一个项目里同时用 PyCharm 内置的虚拟环境创建工具和 uv 创建工具。混用的结果通常是 PyCharm 一直提示“当前解释器不在项目路径中”,或者解释器列表里出现一堆重复的 venv。统一的做法:项目里只保留一个.venv,由 uv 创建和管理,PyCharm 只负责“使用它”。
其次,如果你在 PyCharm 里执行终端命令,要确保终端环境里没有手动激活过其他虚拟环境。比如你原本在一个叫venv的环境里,又突然uv sync,很容易误以为依赖装错了。最稳妥的办法是,在 PyCharm 终端中先执行deactivate,再让 uv 独立管理项目环境。
最后提一点:PyCharm 对 uv 的支持在逐步增强,新版里可以直接在解释器设置里看到 python 的版本管理,但仍然建议以“加载现有解释器”为主,减少额外配置。
5.4 下载慢、超时、镜像源失效怎么办
Windows 上配置了国内镜像源之后,依然可能遇到问题,最常见的是镜像源同步延迟导致的“包找不到”。
排查思路如下:
- 如果你设了环境变量
UV_INDEX_URL,但又临时用了--index-url指定其他源,以命令行为准。 - 如果某天一个包突然“找不到”,先去官方 PyPI 页面看看这个包是否真的存在。如果存在,多半是镜像没同步,临时换回官方源确认。
- 如果下载到一个坏的缓存,可以加
--no-cache参数绕过缓存重新下载:
uv pip install --no-cache flask- 如果你想同时使用多个源,比如一个主源一个备用源,可以在 pyproject.toml 里配置
[[tool.uv.index]],类似:
[[tool.uv.index]] name = "mirror" url = "https://mirrors.aliyun.com/pypi/simple/" default = true日常情况下我推荐只配一个高质量主源,不要贪多。多个索引会增加解析时间,而且一旦某个源缺包,解析器可能提前报错而不是继续尝试下一个源。
5.5 uv 大版本升级后项目不兼容怎么办
uv 迭代速度很快,我遇到过从旧版本项目升级到新版本 uv 后,uv sync报锁文件格式不兼容的情况。这通常表现为提示 uv.lock 版本过旧,或者某个参数已废弃。
处理方式是先升级 uv:
uv self update然后重新生成锁文件:
uv lock如果项目环境的依赖已经乱七八糟,最简单粗暴但有效的方法是删掉.venv和uv.lock,重新执行:
uv sync别心疼,锁文件和虚拟环境本身都是可再生资源,关键是代码和 pyproject.toml 里的依赖声明没丢。当然,如果你正在和其他人协作,最好先更新锁文件并确认依赖版本变化不会破坏现有代码,再提交到版本库。
还有一个小技巧:升级 uv 后,如果某个旧项目暂时不想动,可以看一下 uv 的发布说明,确认你使用的命令是否被重命名或拆分了。比如 early access 阶段有些命令后来有调整,现在文档里的命令形式基本稳定了,但偶尔也会有小变动。
写在后面的一点个人体会
我从开始用 uv 到现在,把日常写脚本和项目开发的流程基本都迁到了这套链路上,尤其在 Windows 上受益明显。以前我电脑里装着好几个 Python 版本,又有不同的虚拟环境,常常分不清哪个项目用哪个解释器,每次安装依赖都在“等 pip 转圈”,遇到网络波动更是烦躁。uv 把这一串问题都压扁了:一条uv run自动建环境,一条uv add管理依赖,一条uv python pin固定解释器版本,剩下的时间基本都在写业务代码。如果你手头正好有老项目受够了安装依赖的折磨,我的建议很直接:先拿一个非核心项目试一下uv pip install -r requirements.txt,感受下载速度之后再决定要不要全面迁移。工具这东西,只有自己实测过,才知道到底值不值得换。