uv虚拟环境管理实战:从创建到切换,告别Python环境混乱
2026/9/19 16:54:42 网站建设 项目流程

如果你和我一样,电脑里同时躺着好几个Python项目,一个要3.8,一个要3.11,还有一个要3.12,那过去的日子大概是这样的:先去官网下载解释器,再手工执行python -m venv,接着激活环境,然后pip install -r requirements.txt,运气不好还会撞上某个依赖编译失败。这套流程我用了好多年,直到换成uv,才意识到虚拟环境管理本来可以更简单。

这篇内容想把uv在虚拟环境上的核心用法完完整整串一遍:venv创建、激活与切换、Python版本指定,以及和PyCharm、VSCode这些编辑器的联动。无论你是刚接触Python的新手,还是被环境问题折磨过的老手,按下面的命令走一遍,应该能省下不少时间。热词里频繁出现的“uv切换环境”“python虚拟环境迁移”“内网机器下载uv”这些场景,文中也都会涉及。

1. 从venv到uv:为什么我放弃了一直用的组合拳

1.1 传统虚拟环境的三个核心痛点

先说痛点,不然你不会理解为什么要换工具。

第一个痛点是解释器版本管理。系统自带的Python通常只有一个版本,但项目却经常要求不同的Python。有人用pyenv,有人手动去官网下载安装包,装完还要自己记住路径,时间一长,机器上的Python版本乱得像一锅粥。

第二个痛点是依赖安装慢。pip install -r requirements.txt对于小项目还行,遇到那种几十个依赖的项目,等待时间能被拉得很长,中间还经常出现网络超时、依赖冲突。更麻烦的是,它不会帮你锁住完整依赖树,昨天还能跑的项目,今天重装环境可能就起不来了。

第三个痛点是“激活环境”这件事本身。Windows和Linux的激活命令不一样,普通用户容易在PowerShell执行策略、PATH顺序上踩坑。有时候辛辛苦苦建好环境,隔两周再打开终端,已经忘记该source哪个目录了。

1.2 uv把哪些原本分离的工作合并了

uv最大的价值,是它把“Python解释器下载”“虚拟环境创建”“依赖安装”“依赖锁定”这几件事合并到了一个工具里。

过去我要为某个项目准备一套环境,至少需要三步:先装解释器,再建venv,最后激活并装包。uv出现后,这套流程被压缩成了两条命令:uv venv --python 3.12uv run

我用一个对比表格来说明它和传统工具的区别:

功能venv + pipcondauv
创建虚拟环境python -m venv .venvconda create -n envuv venv
指定Python版本需要先手动安装conda create -n env python=3.12uv venv --python 3.12
依赖安装速度串行下载,较慢取决于channel并行下载 + 全局缓存,明显更快
依赖锁定无内置锁文件一般靠手工导出自动生成uv.lock
解释器下载不管通过conda源管理uv python install 3.12直接管
激活依赖需要手动activate需要手动activateuv run可以跳过激活步骤

1.3 uv快,不只是因为用了Rust

很多人说uv快是因为Rust写的,这个说法对了一半。Rust让它启动开销低,但真正让安装变快的是设计上的优化:它默认采用并行下载,同时依赖一个全局缓存目录,同一个包在不同项目里不需要反复从网络拉取。再加上它一次性解析完整依赖树并写入uv.lock,后面任何一次同步都只需要按照锁文件执行,不会每次重新碰运气。

这里也解释了为什么团队协作时uv很值钱:新同事拿到项目,只要机器上有uv,一条uv sync就能把环境还原到和你完全一致的状态。这个优势在“虚拟环境迁移”场景下特别明显,不需要再靠requirements.txt去猜依赖版本。

2. 安装uv:从一键命令到离线分发,不同环境的落地方案

2.1 在线环境:Windows、Linux、macOS各有各的装法

uv的安装方式非常灵活,推荐的做法是直接使用官方提供的安装脚本。

Windows下,打开PowerShell执行:

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

Linux或macOS下,在终端执行:

curl -LsSf https://astral.sh/uv/install.sh | sh

不想用脚本也没问题,因为uv本身发布在PyPI上,所以也可以当普通Python包来安装:

pip install uv

macOS用户还可以用Homebrew:

brew install uv

Windows用户如果装了winget,也可以:

winget install --id astral-sh.uv

我的建议是优先用官方脚本,因为它会把uv装到一个独立目录,并且把二进制放到PATH里。如果你用pip install uv,那说明你的机器上已经存在一套Python环境,这与uv想解决的“先有鸡还是先有蛋”问题有点冲突——我们想用uv来管理Python,结果还要先靠Python装uv。

2.2 离线和内网机器怎么装uv

热词里频繁出现“内网机器下载uv”“ubuntu离线安装uv”,这确实是很多人会撞上的场景。内网机器通常没有办法直接访问下载地址,解决方案的关键在于:uv本身是一个编译好的单文件二进制,不像很多工具需要一堆运行时依赖。

具体操作分两步。第一步,在一台能联网的机器上,去uv的GitHub Releases页面下载对应平台、对应架构的压缩包。比如Linux x86_64就下载uv-x86_64-unknown-linux-gnu.tar.gz,Windows就下载对应的zip包。第二步,把压缩包拷到内网机器上,解压后把里面的uv(Windows下是uv.exe)放到一个已经在PATH里的目录,比如/usr/local/binC:\Windows\System32

然后在终端验证:

uv --version

能输出版本号就说明安装成功。这种方式不需要安装任何额外依赖,非常干净。

如果你在内网机上已经有Python环境,更简单的方式是在联网机器上执行:

pip download uv -d ./packages

再把packages目录整个拷到内网,内网执行:

pip install --no-index --find-links=./packages uv

实测下来,前一种方式更适合“内网机器本身干净”的场景,而后一种方式适合那些已经在用pip管理工具链的团队。

2.3 装完提示“uv不是内部或外部命令”的真相

这个问题出现频率极高。安装脚本并不会把uv放到Windows已经默认存在的PATH目录,而是装到用户目录下,比如C:\Users\你的用户名\.local\bin,或者%USERPROFILE%\.local\bin。脚本结束时如果检测到这些路径不在PATH里,会提示你手动添加,很多人没注意就直接关掉终端,重新打开之后自然找不到命令。

解决方法有两个:自己把~/.local/bin加入PATH,或者干脆重新开一个终端再试一次。macOS和Linux下通常是~/.local/bin,如果你用Homebrew装的则不需要关心这个。

3. uv venv创建虚拟环境:常用参数和目录行为

3.1 最基础的创建行为

在任意项目目录下执行:

uv venv

uv会在当前目录创建一个.venv文件夹,并输出类似下面的信息:

Using CPython 3.11.9 Creating virtual environment at: .venv

创建完成后,虚拟环境的本体就在项目文件夹里,路径是.venv。很多人问“虚拟环境建到哪里去了”,答案就在这里,它没有注册到任何全局列表里,本质就是一个文件夹。这个思维很重要:它意味着删除环境直接删掉.venv目录即可,不需要像conda那样专门执行conda env remove

如果你不想用默认的.venv目录名,也可以指定路径:

uv venv myenv

这会在当前目录下生成myenv文件夹。不过我不建议这么做,因为uv很多命令默认会去寻找项目下的.venv,用自定义路径反而会让后续操作变得别扭。

3.2 指定Python版本:--python参数的几种写法

uv venv最实用的地方在于可以直接指定Python版本:

uv venv --python 3.12

这个命令的意思是:用3.12版本的解释器创建虚拟环境。注意,它不仅仅是把一个3.12的解释器路径塞进环境里,而是会先检查本机有没有3.12,如果没有,会给出类似“Python 3.12 not found”的提示。

如果你的机器上已经通过uv安装过多个Python版本,还可以用更精确的方式:

uv venv --python 3.11 uv venv --python 3.12.4 uv venv --python pypy@3.10

甚至可以用范围表达式:

uv venv --python ">=3.10,<3.12"

这种范围写法在多个Python版本并存时非常实用,uv会自己挑一个符合条件的最新版本。如果你最终想确定到底用了哪个版本,创建完成后执行:

.venv/bin/python --version

Windows下执行:

.venv\Scripts\python.exe --version

这里需要提醒一下:uv venv在本地找不到指定版本时,不会像uv run那样主动下载。所以实操中更顺手的组合是“先uv python install 3.12,再uv venv --python 3.12”,具体在第五章展开。

3.3 --seed和--system-site-packages:两种不常用但能救场的参数

uv设计的目标是快,所以它创建的虚拟环境默认不带pip。这通常是好事,因为uv有自己的一套依赖安装命令。但有些老项目、老脚本必须依赖pip,或者你想在虚拟环境里执行pip install,这时创建时加一个--seed参数即可:

uv venv --seed

加上之后,虚拟环境里会预装pip、setuptools和wheel,相当于补齐了venv默认行为。

另一个参数是--system-site-packages

uv venv --system-site-packages

加了它之后,虚拟环境会“继承”系统Python环境里的已安装包。什么时候会用到呢?比如你在一台内网机器上,系统Python里已经装了大量依赖,但你又不想花时间在虚拟环境里重新装一遍,这时候可以让虚拟环境直接看到系统包。代价是隔离性变差,容易搞不清某个包到底是环境里的还是系统里的,除非有很强的理由,否则我不建议日常使用。

3.4 Windows路径带空格:一个让人头疼的老问题

热词里有一条特别具体的信息:d:\python project\.venv\scripts\python.exe" "d:\python project\main.py" did。一看就知道是Windows路径带空格导致的执行报错。

问题的根源是命令行解析规则。如果你直接执行:

d:\python project\.venv\Scripts\python.exe d:\python project\main.py

系统会把命令拆成d:\pythonproject\.venv\Scripts\python.exe等几段,然后报“d:\python 不是内部或外部命令”。正确的做法是整个路径加双引号:

"d:\python project\.venv\Scripts\python.exe" "d:\python project\main.py"

但如果你每天都这么敲,体验确实很差。更好的办法是进入项目目录后直接执行:

uv run python main.py

这样根本不需要关心解释器路径,uv会自动定位到.venv里对应的解释器。关于uv run的详细用法,下一章会重点讲。

4. 激活、退出与“不激活也能用”的切换逻辑

4.1 Windows下的激活命令和那些经典报错

Windows环境下,进入项目目录后,常规激活命令是:

.venv\Scripts\activate

等一下,这个命令在cmd里和PowerShell里的表现不完全一样。

在cmd里直接执行activate.bat

.venv\Scripts\activate.bat

在PowerShell里,应该执行activate.ps1

.venv\Scripts\Activate.ps1

最常见的报错是:

无法加载文件 .venv\Scripts\Activate.ps1,因为在此系统上禁止运行脚本

这是PowerShell执行策略的限制。如果你确定当前项目的脚本是安全可信的,可以为当前用户放开限制:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

如果公司电脑有统一策略限制,改不了,那就退回cmd窗口,用activate.bat激活,或者干脆用后面的uv run方案。

4.2 Linux/macOS下的激活命令

Linux和macOS下就简单多了:

source .venv/bin/activate

激活成功后,终端提示符前面会出现(.venv)前缀,这时你再执行python,系统会优先使用虚拟环境里的解释器。离开环境用:

deactivate

这里有一个非常容易被新手忽略的点:source命令只在当前终端会话生效,关掉这个终端,下次打开还得重新激活。很多人因此忘了激活、装包装到系统环境里,最后搞得一团糟。

4.3 更推荐的用法:uv run与uv shell,跳过手动激活

uv的设计者和传统venv有一个重要分歧:他们认为显式激活这个动作本身就是多余的心智负担。所以uv提供了两个更省事的命令:

uv run python main.py

这个命令的运行逻辑是:在项目目录下,检测是否存在.venv,不存在则创建,然后直接在虚拟环境里执行后续命令。加了--python参数还能临时用指定版本:

uv run --python 3.12 python main.py

如果你就是想进入一个已经激活的交互式shell,可以用:

uv shell

它会让你进入当前项目的虚拟环境shell,效果等同于手动source .venv/bin/activate

我从实际使用的感受来讲:日常开发,uv run比手动激活舒服太多。它不用考虑“我现在在哪个终端”“环境是否已经激活”这些问题,项目目录下直接跑命令,永远不会搞混。

4.4 切换Python版本时的环境重建

热词里有“uv 切换环境”和“uv 删除环境”,这两个操作经常和版本切换绑在一起。

当你在项目根目录执行:

uv python pin 3.11

uv会在项目下生成一个.python-version文件,这就是告诉uv:这个项目以后用3.11。但这并不会把已经创建好的.venv自动升级成3.11,你在命令行里敲uv run python --version会看到一个现象:任务规划器里的版本已经变成了3.11的约束,但现有环境里的解释器可能还是旧的3.12。

要彻底切换,最稳妥的做法是重建环境。先删掉旧环境:

Remove-Item -Recurse -Force .venv # Windows PowerShell rm -rf .venv # Linux/macOS

然后重新创建并同步:

uv venv --python 3.11 uv sync

这个“删除环境、重建环境”的思维在uv里很自然,因为.venv只是一个文件夹,删掉重来通常只需要几秒钟。相比conda的环境导出、导入,这种方式干净且没有残留。遇到环境疑似损坏时,我的第一反应也是删掉重建,而不是去翻那些排查教程。

5. 让uv来管理Python解释器:版本指定从入门到实用

5.1 为什么要让uv管理解释器

Python版本管理的痛点,往往在“第一台机器上没问题,换台机器就出事”。原因大多是解释器版本不一致。uv的解法是让解释器本身也成为“项目管理的一部分”。

在uv的体系里,你不需要先装好Python再创建环境,而是可以让uv负责解释器的安装。最直接的好处是:可以同时管理3.10、3.11、3.12等多个版本,每个项目各取所需,互不干扰。

5.2 常用命令:list、install、uninstall、find、pin

先看本机有哪些Python可用:

uv python list

这个命令会同时列出已安装的版本和可以下载安装的版本。想安装某个版本:

uv python install 3.12

如果你希望装一个最新补丁版:

uv python install 3.11

uv会自动解析到当前3.11系列的最新补丁版本。

反过来,想卸载:

uv python uninstall 3.12

想查看当前项目最终会用哪个Python,可以用:

uv python find

这个命令不是猜的,它会根据.python-versionpyproject.toml里的requires-python字段、以及.venv当前使用的解释器综合判断。排查问题时,uv python find能帮你快速定位“为什么用的是这个版本”。

钉住项目版本用:

uv python pin 3.11

5.3 .python-version文件:让版本约束跟着项目走

uv python pin 3.11会生成一个.python-version文件,内容很简单,就是一行3.11。这个文件最好提交到Git里,这样所有拿到项目的同事,只要执行uv run,uv就会自动识别并切到3.11。

这里有一个组合用法值得记住:项目根目录有.python-version,里面写3.11,然后在pyproject.toml里声明requires-python = ">=3.10,<3.12"。那么uv会优先参考.python-version的精确指定,同时用requires-python校验这个选择是否符合项目声明。两者配合,团队里再也不会出现“我本地能跑你本地不能跑”的经典矛盾。

5.4 离线环境的版本管理建议

内网环境想直接让uv下载Python解释器,通常是做不到的,因为Python安装包默认从Python官网拉取。有两条路可以绕:

第一条,如果你内网系统里已经预置了某个Python版本,可以让uv把它当成受管理的解释器来用。uv支持把系统Python链接进自己的管理目录,相关思路可以在uv python install --help里查--link-system-python选项。不同版本参数略有差异,建议以你安装的版本帮助输出为准。

第二条,在联网机器上用uv python install 3.12装好,然后把uv负责解释器的缓存目录整个拷贝到内网机器,同时把内网的UV_PYTHON_INSTALL_DIR环境变量指到对应位置。这条操作对缓存目录的路径要求比较高,适合团队内统一运维的场景,平时单机使用不必折腾。

6. 和PyCharm、VSCode联动,以及脚本调用不再报错

6.1 在PyCharm里选择uv创建的环境

PyCharm的新版和旧版界面略有差异,但逻辑一样:安装完依赖后,在Settings里进入项目解释器配置。

路径通常是:File -> Settings -> Project: 你的项目名 -> Python Interpreter,点击右侧的齿轮或“Add Interpreter”,选择“Existing environment”,然后在解释器路径里浏览到项目下的.venv\Scripts\python.exe(Windows)或.venv/bin/python(Linux/macOS)。

在这个界面里,很多人会误选到.venv文件夹根目录,那个不是解释器,PyCharm也不会接受。记得选到具体的python.exepython文件。

6.2 在VSCode里选择uv创建的环境

VSCode需要先安装Python扩展,然后用快捷键打开命令面板:

快捷键:Ctrl+Shift+P(Windows/Linux)或Cmd+Shift+P(macOS)

输入:

Python: Select Interpreter

在列表中选择.venv对应的解释器路径即可。如果列表里没出现,可以点击“Enter interpreter path”,手动浏览到.venv\Scripts\python.exe

这里有个经验:VSCode一旦选择了某个解释器,会在项目根目录生成.vscode/settings.json,把该路径写进去。如果你的项目换机器后路径变了,记得更新这个文件或者重新选择一次,否则VSCode会一直指向老路径。

6.3 路径带空格的正确调用姿势

回到热词里那条Windows报错信息,你注意到没有,用户其实是想直接用解释器路径运行脚本,但因为路径里有空格,命令被拆断。正确写法是每个路径单独加双引号:

& "d:\python project\.venv\Scripts\python.exe" "d:\python project\main.py"

在cmd里可以不加&,PowerShell里建议加。

不过以我现在的习惯,这种场景我会直接:

cd "d:\python project" uv run python main.py

uv run会自己找到.venv里的解释器,完全绕开路径空格问题。这也是我在团队内部推广uv时最爱展示的一个细节——“你不用关心解释器在哪,只要你人在项目目录里,uv就知道该干什么”。

6.4 uv run --with临时依赖:适合调试和一次性脚本

还有一个很实用的场景:你手上有一个Python脚本,需要依赖某个库,但你不想为它专门创建环境,也不想污染当前环境。看这个:

uv run --with requests python fetch_data.py

uv会临时创建一个包含requests的隔离环境,执行完这个命令后,环境自动丢弃。对于写爬虫脚本、写临时分析脚本的场景,这个能力比手工创建环境高效太多。

7. 我实际踩过的坑和排查思路

7.1 activate.ps1无法加载,提示“禁止运行脚本”

这是Windows用户最常见的第一个坑。根本原因是PowerShell默认不允许执行本地脚本。

处理办法前面提到过,给当前用户放开RemoteSigned策略:

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

如果你不想到处折腾执行策略,也可以用cmd来激活,或者执行:

powershell -ExecutionPolicy ByPass -c ".\.venv\Scripts\Activate.ps1"

但最省心的还是把激活这件事交给uv run。

7.2 明明激活了环境,python却还是系统版本

这个现象通常发生在你执行激活命令之后,发现python --version显示的是系统Python或其他环境里的版本。

排查链路是:先确认当前虚拟环境里有没有python:

ls .venv/bin/python

Windows:

dir .venv\Scripts\python.exe

如果文件存在,再看环境变量:

echo $env:PATH

Linux/macOS:

echo $PATH

.venv相关路径是不是在最前面。如果不在,很可能是激活脚本没有生效,或者你开了多个终端,当前终端没有source到新的环境。我见过不少人是在VSCode的集成终端里同时开了好几个标签页,激活了A标签,却在B标签里执行python,自然还是老版本。

7.3 uv sync和pip install混用,环境状态乱了

装了uv之后,我一度在同一个项目里一会儿用uv add,一会儿又顺手pip install某个包,结果发现uv run检测到的依赖和实际site-packages里的内容对不上。

原因是uv的依赖状态是通过uv.lock和pyproject.toml来维护的,pip install直接往site-packages里塞的包,并不在锁文件里。下次uv sync时,多出来的包会被清掉,项目就跑不起来了。

教训很简单:项目用了uv,就统一用uv adduv removeuv sync来维护依赖,不要混用pip。如果已经有老项目依赖requirements.txt,可以用uv pip install -r requirements.txt过渡,但长期来看还是迁移到uv的依赖管理模型更省心。

7.4 缓存目录占用空间越来越大

uv有全局缓存,装过的每个包都会留在缓存里,方便以后复用。在实际使用中,这个目录会慢慢变大,尤其当你频繁切换Python版本、尝试不同包时。

查看缓存位置:

uv cache dir

清理没用的缓存:

uv cache prune

清空全部缓存:

uv cache clean

cache prunecache clean温和,它只删掉那些不再有环境引用的缓存包,不会影响已创建的环境。建议日常用prune就够。

7.5 一直提示找不到指定的Python版本

如果你的项目根目录有.python-version文件,并且里面写了一个本地没有装的版本,那么任何时候执行uv runuv venv --python xxx都会报“找不到版本”。

处理方式是先看看当前钉住的版本:

cat .python-version

然后再安装对应版本,或者改成你本地已有的版本:

uv python install 3.12

还有一种情况是pyproject.toml里写了requires-python = ">=3.13",但你的机器上根本没装3.13,uv也会提示找不到。处理方式一样,装一个能满足约束的版本即可。

这个检查逻辑很容易记住:uv不会在你毫不知情的情况下偷偷下载解释器,它默认遵循文件里的约束;当约束和实际环境不一致时,它会明确报错。这时候不要想着绕开它,老老实实把对应版本装好才是正路。

个人体会

切换到uv之后,我最大的收获其实不是速度本身,而是对虚拟环境这件事的理解变了。过去我觉得环境是一个需要小心伺候的、很容易出问题的东西,现在我只把它当成一个普通的文件夹——坏了删掉重建,几秒钟搞定,完全不心疼。所有和Python版本相关的决策都被固化到了.python-version和pyproject.toml里,换一台机器、换一个同事来接手,成本都低很多。

最后分享一个小技巧:如果你在Windows上经常被路径空格、激活脚本折腾,不妨养成一个习惯,所有Python项目目录都用一个不带空格的短路径,比如E:\projects\demo。很多玄学问题会直接消失。然后,日常命令统一用uv run,需要进交互式调试再用uv shell,这样你既能享受虚拟环境的隔离性,又不用背着激活脚本那套繁琐的心智负担。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询