如果你和我一样,电脑里同时躺着好几个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.12和uv run。
我用一个对比表格来说明它和传统工具的区别:
| 功能 | venv + pip | conda | uv |
|---|---|---|---|
| 创建虚拟环境 | python -m venv .venv | conda create -n env | uv venv |
| 指定Python版本 | 需要先手动安装 | conda create -n env python=3.12 | uv venv --python 3.12 |
| 依赖安装速度 | 串行下载,较慢 | 取决于channel | 并行下载 + 全局缓存,明显更快 |
| 依赖锁定 | 无内置锁文件 | 一般靠手工导出 | 自动生成uv.lock |
| 解释器下载 | 不管 | 通过conda源管理 | uv python install 3.12直接管 |
| 激活依赖 | 需要手动activate | 需要手动activate | uv 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 uvmacOS用户还可以用Homebrew:
brew install uvWindows用户如果装了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/bin或C:\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 venvuv会在当前目录创建一个.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 --versionWindows下执行:
.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:\python和project\.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.11uv会在项目下生成一个.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.11uv会自动解析到当前3.11系列的最新补丁版本。
反过来,想卸载:
uv python uninstall 3.12想查看当前项目最终会用哪个Python,可以用:
uv python find这个命令不是猜的,它会根据.python-version、pyproject.toml里的requires-python字段、以及.venv当前使用的解释器综合判断。排查问题时,uv python find能帮你快速定位“为什么用的是这个版本”。
钉住项目版本用:
uv python pin 3.115.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.exe或python文件。
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.pyuv run会自己找到.venv里的解释器,完全绕开路径空格问题。这也是我在团队内部推广uv时最爱展示的一个细节——“你不用关心解释器在哪,只要你人在项目目录里,uv就知道该干什么”。
6.4 uv run --with临时依赖:适合调试和一次性脚本
还有一个很实用的场景:你手上有一个Python脚本,需要依赖某个库,但你不想为它专门创建环境,也不想污染当前环境。看这个:
uv run --with requests python fetch_data.pyuv会临时创建一个包含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/pythonWindows:
dir .venv\Scripts\python.exe如果文件存在,再看环境变量:
echo $env:PATHLinux/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 add、uv remove、uv sync来维护依赖,不要混用pip。如果已经有老项目依赖requirements.txt,可以用uv pip install -r requirements.txt过渡,但长期来看还是迁移到uv的依赖管理模型更省心。
7.4 缓存目录占用空间越来越大
uv有全局缓存,装过的每个包都会留在缓存里,方便以后复用。在实际使用中,这个目录会慢慢变大,尤其当你频繁切换Python版本、尝试不同包时。
查看缓存位置:
uv cache dir清理没用的缓存:
uv cache prune清空全部缓存:
uv cache cleancache prune比cache clean温和,它只删掉那些不再有环境引用的缓存包,不会影响已创建的环境。建议日常用prune就够。
7.5 一直提示找不到指定的Python版本
如果你的项目根目录有.python-version文件,并且里面写了一个本地没有装的版本,那么任何时候执行uv run或uv 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,这样你既能享受虚拟环境的隔离性,又不用背着激活脚本那套繁琐的心智负担。