谈到 Python 环境,我相信不少人都经历过这类收拾烂摊子的时刻:电脑里 Python 版本堆了三四个,不同项目依赖的第三方库互相蚕食,一次 pip install 报错能排查一下午;换了新电脑更是灾难,装完 Python 装包,装完包装依赖,装完依赖又发现版本对不上。所以当 python 包管理工具 uv 出现的时候,身边很多同事都主动把环境这套全部换成了 uv。这个由 Astral 团队用 Rust 编写的工具,把虚拟环境、依赖解析、多版本 Python 下载、脚本运行这些散落的功能全部收编到了同一套命令里,装上它之后,我处理 Python 项目几乎不再单独碰 pip、venv 和 pyenv。无论你是刚入门 Python 的新手,还是天天被依赖折磨的老手,这篇文章都会从安装讲起,把 uv 的日常操作、项目实战和典型坑位完整梳理一遍,希望能帮到正打算换工具的你。
1. uv 凭什么替代 pip、venv、pyenv 这一套组合
1.1 传统 Python 环境管理到底笨在哪
先说说以前的标准姿势。创建虚拟环境用 python -m venv,装包用 pip install,管理不同 Python 版本用 pyenv,想让命令行工具安装干净一点还得上 pipx。每一层工具单独看都能用,拼在一起就不是那么回事了。
问题出在几个地方。第一,工具与工具之间各管各的,项目环境的解析链特别长。比如 pyenv 负责切全局版本,virtualenv 负责建虚拟环境,pip 负责装依赖,一旦项目里需要多个 Python 版本交叉验证,一套流程要拆分出好几个命令去执行,心智负担很重。第二,pip 自身的依赖解析能力较弱。pip install flask 这类简单场景倒是没毛病,可一旦包多起来,版本冲突要么靠手动试,要么由 pip-tools 这类外部工具辅助,过程非常煎熬。第三,环境复制能力很弱。新人拿到项目,要先读 README 手动装依赖,装完还不一定和作者环境一致,因为 requirements.txt 里通常只写了顶层依赖,没有锁定每个传递依赖的确切版本。
uv 的思路是把这堆事情统一起来。它自己实现了 venv 的创建,自己实现了依赖解析和安装,自己实现了 Python 版本的下载和切换。项目经理只需要记住 uv init、uv add、uv run、uv sync 这几个命令,就能完成整个环境的创建、依赖锁定、部署和复现。用一次就能感受到,这个工具不是为了炫技,而是真的在解决工程化里的实际问题。
1.2 速度差距是底层设计决定的
很多人第一次跑 uv 都被速度吓到:一个中等规模的 Django 项目,几十个依赖从零装好,可能十几秒就完成了。同样的事情如果交给 pip 来做,大概率还要经历一段漫长的进度条。
速度快不是玄学,主要原因有三个。第一,uv 本身用 Rust 编写,没有全局解释器锁的干扰,并发解析和并发下载的能力明显强于以 Python 实现的包管理器。第二,uv 引入了全局缓存,同一个包同一版本,在不同项目里首次下载后就不再重复下载,而是通过硬链接把文件复制到新的虚拟环境里,省掉了网络传输和磁盘写入。第三,它的依赖解析器内部做了很多优化,可以并行去请求包元数据并进行版本冲突检测,而不是像传统工具那样逐步试探。
用我自己的经历举例,之前一个爬虫项目需要 requests、BeautifulSoup4、lxml、pandas 等十几个依赖,在某个旧环境里 pip 装一遍大概要两三分钟,偶尔还会因为 lxml 的二进制 wheel 解析慢而卡住。换到 uv 之后,实现同样的依赖安装基本在几秒内结束,尤其 lxml 这种带二进制的包能直接命中缓存,那种等待的心态一下就没了。
提示:uv 的全局缓存在 Linux 和 macOS 上默认位于
~/.cache/uv,在 Windows 上位于%LOCALAPPDATA%\uv\cache。如果你需要跨机器复用缓存,可以通过UV_CACHE_DIR环境变量指定一个共享目录。
1.3 不是推翻重来,兼容性设计得很聪明
看到 uv 把自己定位成 pip、venv、pyenv 的替代品,可能有读者会担心原来那套工作流是不是全部作废了。其实 uv 在兼容性上做了很多考虑。
最典型的是 uv pip 子命令。它基本模拟了 pip 的常用接口,原先 pip install requests、pip install -r requirements.txt、pip list、pip freeze 这类操作,直接换成 uv pip install requests、uv pip install -r requirements.txt、uv pip list、uv pip freeze 就能用。从原体系迁移过去,不需要把项目里所有历史命令都重写一遍。
另外 uv 创建的 .venv 目录里,使用的是标准的 Python 虚拟环境结构,bin、lib、include 这些目录布局和 python 自带 venv 基本一致。VSCode 的 Python 插件、PyCharm 的解释器识别都能直接认出来,不会出现 IDE 不认环境的情况。这也是我最早敢在真实项目里应用 uv 的原因,最坏情况是回到老命令,项目代码不会受损。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种安装方式:Windows、Ubuntu、内网离线都能搞定
2.1 Windows 安装三步走与 PATH 配置
Windows 上安装 uv 最直接的方式是打开 PowerShell 执行:
powershell复制irm https://astral.sh/uv/install.ps1 | iex
执行完之后,uv 会被装到 %USERPROFILE%\.local\bin 目录下。安装成功可以关掉当前终端重开一个,然后执行 uv --version 验证。
如果你电脑上已经有 Python 和 pip,也可以用更传统的方式安装:
bash复制pip install uv
这种方式适合那些暂时不想改动系统 Python 的读者,因为 uv 本身就是一个 Python 包,装好之后直接用即可。还有一条路子是 winget:
bash复制winget install astral-sh.uv
如果 winget 搜索不到,可以先执行 winget search uv 查找确认。
装完之后最容易踩的坑就是 PATH。如果终端里输入 uv 提示"不是内部或外部命令",说明 %USERPROFILE%\.local\bin 没有加入系统环境变量。可以手动去系统设置里添加,也可以直接把 uv 目录放到一个已经在 PATH 里的目录中。我的建议是在用户环境变量里把这个目录加上,因为它之后还可能存放其他命令行工具。
提醒:Windows 的用户环境变量修改完,需要重新打开终端窗口才能生效。如果不想重启 PowerShell,可以执行
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + $env:Path手动刷新一次。
2.2 Ubuntu 和 Linux 安装的两种方式
在 Ubuntu 上安装 uv 的思路类似,官方提供了一行脚本:
bash复制curl -LsSf https://astral.sh/uv/install.sh | sh
安装完成后,uv 会出现在 ~/.local/bin。有些 Linux 发行版的 ~/.local/bin 默认不在 PATH 里,因此安装后先执行 source ~/.bashrc 或 export PATH="$HOME/.local/bin:$PATH",再验证版本。
如果你使用的是 Ubuntu 24.04 或更新的版本,还可以直接用包管理器安装:
bash复制apt install uv
用 apt 安装的好处是可以跟着系统渠道一起升级,缺点则是版本可能不是最新的。如果你很在意 uv 的新特性,比如新的依赖分组支持、Python 版本卸载命令等,我还是建议走官方脚本,或者到官网下载二进制压缩包手动放置。
另外,如果服务器上没有 curl 和网络环境受限,可以下载 uv 的 .deb 包或二进制 tar.gz 离线安装。这个放到下面的内网场景里一起讲。
2.3 内网机器离线安装 uv 的完整流程
内网机器装 uv 是很多运维和同学关心的场景,我自己也在离线环境里试过几回,完全可以搞定。
先在有网络环境的机器上打开 uv 的官方 GitHub Releases 页面,下载对应平台的压缩包。Linux x86_64 架构下载 uv-x86_64-unknown-linux-gnu.tar.gz,Windows x64 下载 uv-x86_64-pc-windows-msvc.zip,ARM 架构的机器则下载 uv-aarch64-unknown-linux-gnu.tar.gz。把压缩包拷贝到目标机器的 U 盘或内网服务器上。
Linux 下解压后能看到 uv 和 uvx 两个可执行文件。把这两个文件放到 /usr/local/bin 或者服务器有权限的目录里,再确认 PATH 包含该目录即可:
bash复制tar -xzf uv-x86_64-unknown-linux-gnu.tar.gz
sudo cp uv-x86_64-unknown-linux-gnu/uv uv-x86_64-unknown-linux-gnu/uvx /usr/local/bin/
uv --version
Windows 上离线安装更简单,解压 zip 后把 uv.exe 和 uvx.exe 放到自定义目录,再把这个目录加入 Path 环境变量。
内网机器除了 uv 本身,可能还需要离线安装 Python 版本。同样可以在联网机器上下载 Python 构建工具包,然后通过 uv python install --offline 指定本地目录安装。如果内网环境完全隔离,建议在有网机器上把 uv 的全局缓存整个打包拷过去,放到内网机器的缓存目录里,这样绝大多数已缓存的包和 Python 版本都能直接复用,不需要从外部下载。
注意:离线场景下,依赖包尽量打包成 wheelhouse 形式。在有网机器上执行
uv pip download -d wheels -r requirements.txt --python-version 3.12 --only-binary=:all:把依赖的 wheel 全部拉下来,内网机器上再用uv pip install --find-links wheels -r requirements.txt完成安装。这样能避免内网机器临时找不到包源的尴尬局面。
3. 高频操作实战:建环境、装依赖、切版本、删环境
3.1 一套标准项目工作流:init、add、run、sync
uv 对项目型工作流的设计非常顺手。进入一个新目录,先初始化项目:
bash复制uv init demo
cd demo
执行完会发现目录下多出了 pyproject.toml 和 main.py。pyproject.toml 是项目元数据文件,uv 会把依赖声明写在这里面。此时项目还没有创建虚拟环境,uv 会根据需要自动创建。
接下来添加依赖:
bash复制uv add requests
执行这个命令时,uv 会创建 .venv 虚拟环境,解析 requests 及其所有传递依赖,把它们安装进虚拟环境,并自动维护好 pyproject.toml 和 uv.lock。整个过程走下来,你甚至不需要手动去 source .venv/bin/activate,直接运行:
bash复制uv run python main.py
uv run 会确保在正确的虚拟环境下执行命令。如果当前目录还没有 .venv,它会自动创建;如果 pyproject.toml 里新增了依赖但还没同步,它会在运行前自动补齐。所以日常开发里,我基本很少手动激活虚拟环境,全部交给 uv run 去处理。
项目做完依赖也需要同步给别人。uv sync 会根据 uv.lock 一键生成或更新 .venv 环境,整个过程由锁文件驱动,不会有版本漂移。
如果你手头项目还在用 requirements.txt 的方式,uv 也能无缝衔接。可以在项目里继续使用:
bash复制uv pip install -r requirements.txt
但更整洁的做法是把它迁移到 pyproject.toml 里管理:
bash复制uv add -r requirements.txt
迁移完成后,requirements.txt 就可以退役了,后续依赖全部由 uv add 和 uv.lock 管理,依赖状态清晰得多。
3.2 多版本 Python 与环境切换的正确姿势
uv 打包了 Python 版本管理的功能,这也是很多同学关注的点。原来用 pyenv 管理多个 Python 版本,现在用 uv 也可以做到。
先安装需要的版本:
bash复制uv python install 3.9 3.10 3.11 3.12 3.13
查看当前机器上已安装的版本:
bash复制uv python list
如果只想在某个项目里固定用某个 Python 版本,进入项目目录执行:
bash复制uv python pin 3.11
这会在项目根目录生成一个 .python-version 文件。以后在这个目录里执行 uv venv 或 uv run,都会自动使用 3.11 版本,不需要额外指定参数。
想按项目切换环境也很简单。比如项目 A 需要 3.10,项目 B 需要 3.12,各自目录下 pin 好版本,各自执行 uv sync,环境互不干扰。相比传统方式下用 pyenv 全局版本换来换去,uv 这种按目录绑定版本的设计明显更符合工程化习惯。
如果你的场景是同一套代码需要快速在不同版本下跑一遍,可以临时指定:
bash复制uv run --python 3.10 python script.py
这个命令会用 3.10 创建一个临时环境并运行脚本,不影响项目默认版本。碰到做兼容性测试的场合非常好用。
3.3 删除环境与清理缓存:给磁盘瘦身
uv 本身没有单独的“删除虚拟环境”命令,但这反而是个好消息——环境的本质是一个 .venv 目录,直接删掉就好,你在 Linux/macOS 系统下执行:
bash复制rm -rf .venv
在 Windows 的 CMD 里执行:
bash复制rd /s .venv
删除之后再执行 uv sync 或 uv run,uv 会根据 pyproject.toml 和 uv.lock 重新创建一个全新环境。整个过程轻量透明,不会有残留记录。
除了虚拟环境,uv 的全局缓存也值得定期关注。缓存位置我在前面提过,如果想查看当前缓存目录,执行:
bash复制uv cache dir
想清理所有缓存,可以用:
bash复制uv cache clean
这条命令会把已下载的包和元数据全部清除,后续创建环境时就需要重新下载。如果你不是特别缺磁盘空间,我不建议频繁清理,因为缓存正是 uv 速度快的核心依赖。但如果项目里曾安装过特别大的机器学习依赖,释放几百 MB 甚至几个 GB 也是常有的事。
删除 Python 版本也可以交给 uv。较新版本的 uv 支持:
bash复制uv python uninstall 3.10
如果你的 uv 版本较旧,或者提示没有该命令,直接在 uv python list 显示的安装目录里删除对应文件夹即可。
3.4 用 uv run 和 uvx 跑临时脚本与命令行工具
开发时经常有这样的场景:临时要跑一段脚本,它依赖某个库,但又不值得专门为它创建一个项目。传统做法是在全局环境里 pip install 然后手动清理,这很容易污染环境。uv 有一条很爽的命令:
bash复制uv run --with requests python fetch_data.py
--with 参数会为本次运行临时指定一个额外依赖,运行结束不会污染任何全局环境,也不会在项目里写入依赖声明。换句话说,脚本里 import requests 尽管写,uv 会自动准备好一个带 requests 的环境来执行它。我经常拿这个特性来尝试一段爬虫代码或者数据分析小样,方便到不行。
与之配套的是 uvx,它相当于 Python 生态中的 npx。以前我们安装 ruff 这类命令行工具要用 pipx 或者手动建虚拟环境隔离,现在只需要:
bash复制uvx ruff check .
执行时 uvx 会临时创建环境安装 ruff,直接运行命令,结束后自动清理。高频使用的工具可以用 uv tool install 持久安装:
bash复制uv tool install ruff
这样安装的工具拥有自己的独立环境,不影响项目依赖,实际体验直追 pipx,但安装速度更快。
4. 真实项目复盘:用 uv 从零搭建一个爬虫项目并配置 IDE
4.1 初始化一个爬虫项目并添加核心依赖
为了演示得更具体,我用一个简短的爬虫项目来完整走一遍 uv 的流程。这个项目的目标很简单:抓取一个网页的标题并输出。先初始化:
bash复制uv init crawler-demo
cd crawler-demo
接着安装爬虫相关的依赖:
bash复制uv add requests beautifulsoup4 lxml
这里加 lxml 是因为 BeautifulSoup 解析网页时用它做底层解析器速度更快,而且 uv 对带二进制的包处理很娴熟,下载安装非常快。
然后写一个简单的脚本 crawler.py:
python复制import requests
from bs4 import BeautifulSoup
def fetch_title(url: str) -> str:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
return soup.title.string.strip()
if __name__ == "__main__":
print(fetch_title("https://example.com"))
运行脚本:
bash复制uv run python crawler.py
如果没有意外,终端会先创建 .venv,解析并安装依赖,然后自动执行脚本,最后打印出网页标题。整个过程没有一行 Linux 激活环境的操作,全部被 uv run 包揽了。
我这里刻意控制了脚本的复杂度,因为重点是展示环境管理的体验。当你把注意力从环境切换到业务本身时,才能意识到以前在依赖上花的精力多少是无效成本。
4.2 锁文件与开发依赖分组:让协作不再打架
爬虫项目通常除了运行依赖,还会有测试和代码检查的依赖。uv 把它们分成不同的依赖分组,这样部署环境不会装多余的开发工具。
添加开发用依赖:
bash复制uv add --dev pytest ruff
跑完这条命令后,打开 pyproject.toml 可以看到类似内容:
toml复制[dependency-groups]
dev = [
"pytest>=8.0",
"ruff>=0.6",
]
pyproject.toml 记录的是依赖的约束范围,uv.lock 则记录了解析后的确切版本和哈希。锁文件是 uv 保证环境可复现的核心,必须提交到 git 里。新的同事克隆代码后,只需要一条命令:
bash复制uv sync
就能把运行依赖和开发依赖全部按锁定版本装好。如果只装运行依赖,避免引入 pytest、ruff 这些开发工具,可以执行:
bash复制uv sync --no-dev
在 CI 环境里,我一般会使用:
bash复制uv sync --locked
这个参数会检查 lock 文件与 pyproject.toml 是否一致,如果项目里有人改了依赖但忘了重新 lock,CI 会直接报错,而不是默默装一个没锁定的环境。依赖漂移这种隐性问题,在早期就被拦截住了。
提示:如果你是彻底从 requirements.txt 迁过来的项目,不必追求一步到位。先
uv pip install -r requirements.txt跑通,再慢慢把显式依赖迁移到 pyproject.toml 里,最后用uv lock重建锁文件即可。
4.3 VSCode 和 PyCharm 里正确配置 uv 解释器
命令跑熟了,IDE 这块也需要配顺,不然写代码时索引器和语法提示一样会出问题。
先说 VSCode。安装好 Python 扩展后,在项目里按 Ctrl+Shift+P 打开命令面板,输入 “Python: Select Interpreter”,选择 ./.venv/bin/python(Windows 下是 .venv\Scripts\python.exe)。如果列表里没有,可以直接输入路径手动指定。
也可以直接查看 uv 管理的 Python 具体位置,在终端执行:
bash复制uv run python -c "import sys; print(sys.executable)"
输出结果就是当前项目实际使用的解释器路径,把这个路径填到 VSCode 的 python.defaultInterpreterPath 设置里即可。比如 .vscode/settings.json 里可以写:
json复制{
"python.defaultInterpreterPath": ".venv/bin/python"
}
PyCharm 配置稍微不同。打开 File -> Settings -> Project -> Python Interpreter,点击设置按钮选 Add Interpreter -> Existing,把 .venv/bin/python 加进去。PyCharm 会自动识别项目依赖,代码提示和调试都能正常工作。
值得注意的是,项目每次 uv sync 后,如果依赖结构有变化,IDE 的索引可能需要刷新一下。VSCode 里可以重新加载窗口,PyCharm 里可以点一下编辑器右上角的“Reload All from Disk”,或者干脆重启 IDE。这个刷新过程看起来是个小细节,但很多“代码明明装了为什么还是红波浪线”的问题就是这么解决的。
5. 常见问题与排查技巧实录
5.1 Windows 上 python 命令找不到还弹 Microsoft Store
在 Windows 上,如果终端输入 python 弹出 Microsoft Store 的 Python 安装页面,或者提示 “python was not found; run without arguments to install from the Microsoft Store”,说明系统确实没有配置可用的 Python。这通常是两个原因之一:要么安装 Python 时没有把可执行文件加入 PATH,要么被 Windows 的应用执行别名拦截了。
用 uv 方案可以完全绕开这个问题。先确保 uv 能用,然后执行:
bash复制uv python install 3.12
再在项目里用 uv run python ... 运行代码。uv 自己管理的 Python 版本不依赖系统 PATH,所以即使系统里从来没有任何 Python,项目也能正常工作。如果还是希望在终端直接输入 python 进入交互模式,可以去系统设置里搜索“管理应用执行别名”,把 python.exe 和 python3.exe 的两个开关全部关掉,然后安装官方 Python 并勾选加入 PATH。
5.2 VSCode 报 cannot be resolved against python helper roots
这个报错放在以前很容易让人一头雾水,其实它多出现在 Pylance 无法定位解释器内部辅助文件时。简单说,就是 Pylance 找到了一个 Python 解释器路径,但跟着这个路径去找它依赖的 helper 文件时扑空了。常见于手动指定了一个已被删除的虚拟环境,或者解释器路径指向了非标准位置。
处理思路不复杂。第一步,重新加载窗口:Ctrl+Shift+P,输入 “Developer: Reload Window” 执行。第二步,重新选择解释器:命令面板里执行 “Python: Select Interpreter”,直接选项目下的 .venv/bin/python。第三步,如果还报错,检查 .vscode/settings.json 里的 python.defaultInterpreterPath 是否仍是旧路径。
如果使用 uv 的 uv run 引导,建议先执行 uv run python -c "import sys; print(sys.executable)" 确认当前解释器真实路径,再拿这个路径去配 IDE。这样 Pylance 和 uv 始终指向同一个解释器,helper roots 的报错基本不会再出现。
5.3 离线内网安装 numpy、cv2 这类大包的处理
内网机器上安装第三方库是高频需求,热词里提到“python下载cv2”和“python安装numpy库的方法”。实际场景常常是:服务器不能直接访问外网 PyPI,但项目需要装 OpenCV 和 NumPy 这类带二进制扩展的大包。
最稳妥的方式是 wheelhouse 方案。在有网络机器上执行:
bash复制uv pip download -d wheels -r requirements.txt --python-version 3.12
这会根据目标 Python 版本解析依赖,并把所有 wheel 文件下载到 wheels 目录。把这个目录整体拷贝到内网机器,然后执行:
bash复制uv pip install --find-links wheels -r requirements.txt
注意,下载时最好在本机也安装相同 Python 主版本,确保解析出的 wheel 与内网 CPU 架构和操作系统匹配。如果目标是 Linux glibc 环境,请用相同 glibc 版本的系统来做下载,避免把 musl 版本的 wheel 拷过去导致兼容性失败。
顺带说一句,OpenCV 的包名是 opencv-python,NumPy 就是 numpy。如果你只是临时跑一下图像处理脚本,不需要写进项目依赖,可以用 uv run --with opencv-python --with numpy python script.py 这种方式临时跑通,避免了频繁修改项目依赖的麻烦。
5.4 常见问题速查表
最后把这段时间遇到的各类问题整理成一张速查表,方便各位在实际使用时直接对照。
| 现象 | 可能原因 | 解决方式 |
|---|---|---|
uv 不是内部命令或未找到 |
安装目录不在 PATH | 把 ~/.local/bin 或自定义安装目录加入 PATH |
| 装包时一直卡在解析阶段 | 默认 PyPI 源访问不稳定 | 配置镜像源后重试,具体方式见下方说明 |
No Python version found |
指定版本未安装 | 执行 uv python install 3.12 |
新 clone 项目后 uv sync 失败 |
lock 与 pyproject 不一致 | 检查后重新执行 uv lock 并提交 lock 文件 |
uv run 提示权限不足 |
工具被安装到系统目录 | 优先使用 uv tool install 装用户级工具 |
IDE 不识别 .venv |
解释器路径未指定 | 手动选择 .venv/bin/python 并重载窗口 |
| 缓存目录过大 | 包缓存累积 | 执行 uv cache clean |
关于镜像源,如果你的项目下载依赖特别慢,可以在项目根目录放一个 uv.toml,写入:
toml复制[pip]
index-url = "https://pypi.tuna.tsinghua.edu.cn/simple"
这条配置会让 uv 的 pip 相关命令默认走指定的 PyPI 镜像。如果用的是 uv add 和 uv sync 这类高层命令,也可以把它写成环境变量 UV_DEFAULT_INDEX,指向你信任的镜像源地址。实际配置过一遍就会发现,换源前后下载速度的差别相当直接。
我个人在实际操作中还有一个习惯:新机器到手先装 uv,紧接着 uv python install 3.12,之后所有项目都不再关心系统里有没有 Python、pip 有没有被污染。依赖解析、版本锁定、环境复制这些原本最麻烦的事情,现在基本一条命令解决。如果你还在环境管理的泥潭里打转,我强烈建议找一个不太重要的项目先迁移试水,跑顺之后再整体切换,体验过几分钟装好完整环境的丝滑,基本就回不去了。
