尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

uv add与uv pip install:项目依赖管理的两种关键思路

发布时间:2026/9/26 7:37:57

资讯中心
01
ARTICLE

uv add与uv pip install:项目依赖管理的两种关键思路

uv add与uv pip install:项目依赖管理的两种关键思路
拿到 uv 之后我做的第一件事不是急着uv pip install而是先盯着命令行发了会儿呆。因为 uv 的定位太特殊了它说自己要替代 pip、virtualenv、pyenv、pip-tools结果你打开帮助文档一看竟然有uv add和uv pip install两个都能安装包的命令。这俩到底是重复造轮子还是各有分工我花了一整周做了各种实测结论是这不是同一个功能的两副面孔而是两种完全不同的依赖管理思路。这篇文章就是想把这两条命令掰开揉碎讲清楚。不管你是刚听说 uv、准备从 pip 迁移还是已经在公司项目里用了 uv 但偶尔分不清该敲哪个下面这些内容都能帮你少走弯路。我会先从命令的出身讲起再逐一对比它们对 pyproject.toml、uv.lock、虚拟环境的处理方式最后给出一套可以照抄的项目迁移流程。1. 两种命令的出身uv add 走的是项目工作流uv pip install 只是兼容层1.1 uv add 是 uv 原生项目工作流的一部分先说uv add。它是 uv 在Python 项目管理这个角色里设计的原生命令。你注意看它的环境命令通常运行在有pyproject.toml的项目目录下执行之后会做三件事把依赖写进pyproject.toml、更新uv.lock锁文件、然后把包安装到当前项目的虚拟环境里。这听起来是不是特别像 npm 的npm install没错uv add就是照着现代语言包管理器的逻辑做的。它认为一个项目必须有一个明确的依赖清单清单上的每个包都要有确定的版本范围锁文件里要固定住解析出来的确切版本。这样团队里任何人拉下来代码执行uv sync就能复现出几乎一样的环境。我举个例子你在项目里执行uv add requests它会先把requests写进pyproject.toml的[project.dependencies]里然后重新解析整个依赖树更新uv.lock。如果你当前还没有创建虚拟环境它还会顺手帮你创建一个.venv再把包装进去。这一套动作下来项目的事实状态和声明状态是完全一致的。1.2 uv pip install 是给 pip 用户准备的电梯再看uv pip install。这条命令从字面到行为都是奔着兼容 pip 去的。它存在的意义是让你在不想改项目结构、不想引入 pyproject.toml 和锁文件的情况下也能享受到 uv 的安装速度。所以uv pip install requests做的事情和pip install requests非常像把包装进当前激活的虚拟环境或者当前解释器对应的环境里。它不会修改pyproject.toml不会生成uv.lock也不关心你所谓项目的概念。你可以把它理解成一个高速版的 pip而不是 npm install。这里有一个很关键的信息uv pip install虽然是 uv 提供的命令但它背后用的解析器和安装器还是 uv 自己那套 Rust 实现所以速度优势依然存在。可它的身份是兼容层不是项目工作流的入口。1.3 一句话先记住它们的本质区别我自己总结过一句话非常适合放在脑子里uv add是告诉项目这个包正式进入依赖清单uv pip install只是告诉环境现在把这个包装上。当你后续看到uv sync会把uv pip install装的那些包清掉就会觉得这条规律非常直观。因为 sync 是照着 pyproject.toml 和 uv.lock 来还原环境的清单之外装的东西它一概不认。2. 五个核心差异文件、环境、解析、同步、卸载分别怎么处理2.1 会不会写入 pyproject.tomluv add最核心的行为就是修改pyproject.toml。你可以在项目目录里加一个依赖uv add fastapi打开 pyproject.toml 会看到类似这样的内容[project] dependencies [ fastapi0.115.0, ]注意它不仅写入了包名还会写入一个符合 uv 规则的版本约束。默认情况下 uv 会根据当前已发布版本生成一个下限约束比如0.115.0。这种约束说明你已经验证过这个版本能跑但后续小版本升级在 flake 范围内是允许的。而uv pip install fastapi执行完你去翻 pyproject.toml里面什么都不会变。它只负责把包装进解释器环境任何记录都不会留。2.2 会不会生成 uv.lock这是新手最容易忽略的一条。uv add成功之后项目里会出现一个uv.lock文件。这个文件记录的不是 pyproject.toml 里的模糊约束而是把所有传递依赖的精确版本、哈希值、来源地址全都锁死了。比如你装了 fastapiuv.lock 里会包含 fastapi 的版本、pydantic 的精确版本、starlette 的精确版本以及这些包各自的文件哈希。uv pip install呢它完全不会生成 uv.lock。它就像一个临时工装完就算。如果你希望保持可复现这件事uv pip install是帮不上忙的它连参与都不参与。2.3 作用于项目环境还是当前环境uv add默认把包安装在项目虚拟环境.venv里并且它对这个虚拟环境是有所有权意识的。它知道项目的 Python 版本、环境的路径、锁文件里的内容所以能做到精确同步。uv pip install则继承 pip 的环境语义如果你已经激活了一个虚拟环境它就会装进那个环境如果你没激活任何环境它就会使用当前默认的解释器环境。它不会主动去识别项目里的.venv除非那个环境恰好被激活了。这让uv pip install很灵活你可以用它给 conda 环境装包也可以给系统 Python 装包甚至配合--python参数指定一个解释器。但灵活的另一面是它和项目目录没有绑定关系。2.4 需不需要先有 pyproject.toml在标准流程里uv add需要一个项目上下文。你至少得有一个pyproject.toml不管是uv init生成的还是别的手段创建的。我在实盘里见过不少人不先uv init直接在一个空目录里敲uv add结果不同版本的 uv 处理方式还不太一样有的会提示找不到项目有的会帮你补一个。我的建议很直接永远先uv init或者手动创建 pyproject.toml再考虑uv add。因为uv add不是用来初始化项目的它是给已有的项目加依赖的。uv pip install就不需要这些前置条件。你在任何目录下执行它都能把包装进当前环境哪怕那个目录空空如也。2.5 卸载与清理逻辑的区别对应的卸载逻辑也完全不同。uv add的反操作是uv removeuv remove requests它会从 pyproject.toml 里删除依赖、重新解析锁文件、同步虚拟环境。整个过程依然是声明-解析-同步的项目闭环。uv pip install的反操作是uv pip uninstalluv pip uninstall requests它直接在当前环境下把包装掉不留任何项目痕迹。注意如果你曾经用uv add添加过 requests然后又偷偷用uv pip uninstall requests把它卸了下次跑uv sync它会按照 pyproject.toml 的声明把 requests 重新装回来。这就是混乱的根源。对比项uv adduv pip install写入 pyproject.toml会不会生成/更新 uv.lock会不会默认安装环境项目 .venv当前激活环境需要项目文件一般需要 pyproject.toml不需要对应卸载命令uv removeuv pip uninstall能否被 uv sync 保留能不能3. 对照实操同一批包用两条命令安装现场到底差在哪3.1 uv add 的完整现场为了让你看得更清楚我在一个临时目录里走了一遍完整流程。先初始化项目mkdir demo-uv cd demo-uv uv init然后执行uv add requests控制台会输出类似下面的信息Creating virtual environment at: .venv Resolved 3 packages in 356ms Installed 3 packages in 12ms certifi2024.8.30 charset-normalizer3.3.2 idna3.7 requests2.32.3注意几个细节。第一它自动创建了.venv。第二它显示的是Resolved和Installed这说明它经历了完整的依赖解析过程。第三项目目录下出现了pyproject.toml和uv.lock两个文件。此刻如果你再看 pyproject.toml[project] dependencies [ requests2.32.3, ]而 uv.lock 里已经把 requests、charset-normalizer、idna、certifi 的精确版本全部锁进去了。别人拿到这个仓库只需要执行uv sync就能得到和当前完全一致的环境。3.2 uv pip install 的现场同一个项目里我换个姿势再装一次source .venv/bin/activate uv pip install httpx输出是Resolved 7 packages in 250ms Installed 7 packages in 15ms anyio4.4.0 certifi2024.8.30 h110.14.0 httpcore1.0.5 httpx0.27.2 idna3.7 sniffio1.3.1看到没有httpx 装进去了进程也没有报错。但你现在去看 pyproject.toml[project] dependencies [ requests2.32.3, ]它只有 requests。uv.lock 里也没有 httpx 的任何记录。也就是说这个环境里确实多了一个包但对项目来说它不存在。这个状态特别容易埋雷。假设你把代码 push 到仓库同事 clone 下来执行uv sync他的环境里没有 httpx。如果你忘了更新 requirements 或者没有在文档里说明他跑程序的第一秒就会收到ModuleNotFoundError: No module named httpx。3.3 混用后的还原现场uv sync 会把临时包清掉更典型的场景是这样项目里已经用uv add管理依赖某天你想临时试一个包就顺手uv pip install flask。用完之后你忘了这回事。下次提交代码前或者 CI 里跑uv sync --frozen就会发生一个让你很无语的现象flask 被环境悄悄移除了。因为uv sync的工作方式是差量同步它检查 pyproject.toml 和 uv.lock 里的依赖列表然后和当前环境比对环境里多出来的、不属于锁文件的包会被清理掉。你辛辛苦苦用uv pip install装的东西在 uv 看来就是脏数据。所以我的经验是一个项目内两条命令只能二选一作为日常主力。你要是已经用上了uv add加uv.lock的流程就尽量不要再用uv pip install往同一个环境里塞东西。实在要临时测试用完之后立刻uv sync把环境拉回正轨或者直接建一个临时虚拟环境uv venv /tmp/try-flask source /tmp/try-flask/bin/activate uv pip install flask4. 场景判断什么时候用 uv add什么时候用 uv pip install4.1 需要长期维护、团队协作、可复现部署用 uv add如果你的项目要跑很长一段时间参与的人不止你一个部署环境需要和本地保持一致那没有任何犹豫的余地直接走项目工作流uv init uv add fastapi uv add --dev pytest uv add --group lint ruff uv lock uv syncuv add帮你在 pyproject.toml 里把运行依赖、开发依赖、可选依赖分组管理清楚。uv.lock保证了任何人在任何机器上都能拿到同一套环境。这种确定性的价值在多人项目里远远超过那几秒钟的安装速度。这里有个细节开发依赖用--dev加工具链依赖可以放到独立 groupuv add --dev pytest uv add --group lint ruff生成的 pyproject.toml 会是[dependency-groups] dev [ pytest8.2.0, ] lint [ ruff0.6.0, ]这样生产部署时只需要uv sync --no-dev开发工具就不会被装进生产环境。4.2 临时调试、一次性容器、给某个环境补包用 uv pip install反过来如果你只是想在当前环境里快速装一个包验证想法不想留下任何项目痕迹那uv pip install就是更合适的工具。比如我经常在调试别人的老项目时这么干uv venv .venv source .venv/bin/activate uv pip install -r requirements.txt这里用的是uv pip install -r requirements.txt把现有 requirements.txt 里的包一次性装进虚拟环境。整个过程快得离谱而且没有破坏老项目的结构。等调试完删掉.venv就干净了。4.3 现有 requirements.txt 的项目怎么平滑过渡很多人关心这个问题公司里一堆项目还在用 requirements.txt我能不能直接上 uv其实可以分两步走。第一步先用uv pip install替代 pip 安装享受速度提升。这是风险最低的迁移方式所有环境管理和依赖声明都不变。第二步等你想清楚要用 pyproject.toml 统一管理依赖可以这样把 requirements.txt 转成项目依赖# 先初始化项目 uv init # 把 requirements.txt 里的依赖全部添加进去 uv pip install -r requirements.txt注意上面还只是装包不会生成锁文件。要想真正进入项目工作流你可以用uv add一个一个加或者写一个小脚本把 requirements.txt 解析成uv add参数。手动加一遍通常也就几十条半天内能做完。另外一个思路是直接在 pyproject.toml 里维护依赖然后用uv export --format requirements-txt生成 requirements.txt 给旧的部署脚本用。这样你既享受 new style 的管理又不用一次性改掉所有旧流程。4.4 运行单文件脚本时用 uv run 而不是手动装包还有一种场景值得单独说。你手里有一个 script.py不想为它建项目也不想污染当前环境完全可以这样uv run --with requests --with pandas script.py这条命令会在临时环境里装上 requests 和 pandas跑完脚本后环境自动回收。它既不是uv add也不是uv pip install但它是日常写脚本时最干净的方案。我有时候甚至用它来跑一些一次性数据处理省心得很。5. 环境切换与安装策略Windows、Ubuntu、内网机器和 VSCode 里的实战细节5.1 安装 uv 的官方路径在写具体环境切换之前先把 uv 本身的安装说清楚。很多人卡在最开始。Windows 下最简单的办法是用 wingetwinget install --idastral-sh.uv -e装完重开一个终端执行uv --version验证。也可以用 PowerShell 装官方脚本或者直接下载 msi 安装包。Ubuntu 或 WSL 下面官方脚本是最省事的curl -LsSf https://astral.sh/uv/install.sh | sh脚本会把 uv 安装到~/.local/bin然后自动帮你配置 PATH。如果不想用远程脚本也可以直接下载 release 里的二进制 tar.gz 解压把uv和uvx放到~/.local/bin就行。还有一种非常通用但稍慢的装法pip install uv这种方式的好处是内外网都好处理坏处是 uv 作为 Python 包性能和独立性总没有官方二进制那么彻底。有条件的话我建议优先用官方独立二进制。5.2 内网机器怎么离线安装 uv很多企业内网机器不能直接访问外网这时候离线安装 uv 就成了刚需。核心思路是在能联网的机器上把 uv 的二进制包或者 Python wheel 下载好通过移动介质或者内部文件服务器拷到内网机器上。如果你用官方二进制就下载对应平台的 tarball内网解压后放到 PATH 目录。比如 Linux x86_64 平台tar -xzf uv-x86_64-unknown-linux-gnu.tar.gz sudo cp uv-x86_64-unknown-linux-gnu/uv /usr/local/bin/如果你更习惯 Python wheel可以这样pip download uv --dest ./uv-offline然后把uv-offline/uv-*.whl拷到内网机器继续用 pip 安装pip install uv-*.whl装完同样执行uv --version确认。内网机器如果还需要下载 Python 包就需要配合内部源或者提前把 wheel 打包下载下来这是另一套话题但 uv 二进制本身离线部署起来并不复杂。5.3 uv 多个 Python 版本和虚拟环境的切换uv 被很多人当作 pyenv 的替代品原因就是它把 Python 版本管理也做了。想装一个新的 Python 版本uv python install 3.11项目里指定 Python 版本uv python pin 3.11这个命令会在项目目录下生成一个.python-version文件之后uv sync、uv add、uv run都会默认使用这个版本的 Python。切换环境也不是传统意义上的conda activate而是用uv run来隐式激活uv run python main.py它会自动检查.venv是否存在、Python 版本对不对、锁文件是否最新缺什么补什么然后直接执行脚本。这个体验比手动 source activate 顺畅太多。当然你也可以保留传统习惯source .venv/bin/activate进入虚拟环境之后照样能跑 python 和 pip。uv 并不强制你改变所有习惯。5.4 VSCode 里配置 uv 环境VSCode 经常有人踩环境选错的坑。其实配置思路特别简单先让 uv 把项目环境建好再把解释器路径指向.venv里的 Python。完整流程是cd your-project uv sync等命令跑完你会看到.venv/bin/python这个文件。然后在 VSCode 里按CtrlShiftP选择 Python: Select Interpreter选 Enter interpreter path填入.venv/bin/pythonWindows 上是.venv/Scripts/python.exe如果你用的是 remote 容器或者 SSH路径逻辑也一样。选好之后VSCode 的终端会自动识别虚拟环境代码补全和 Pylance 也能正确读取依赖。补充一个经常被问到的点也可以在.vscode/settings.json里固定默认解释器{ python.defaultInterpreterPath: ${workspaceFolder}/.venv/bin/python }这样团队克隆项目后只要跑过uv syncVSCode 大概率就不会再选错解释器。6. 日常迁移中踩过的坑和我的收尾建议6.1 第一次混用命令后的教训我自己最早切 uv 的时候也干过一件蠢事。项目里已经用uv add管了三十多个依赖后来为了处理一个临时线上问题我直接uv pip install sentry-sdk装完解决问题就忘了。第二天我改了代码正常执行uv sync然后发现环境里的 sentry-sdk 不见了代码 import 直接失败。当时第一反应是怀疑 uv 有 bug后来才反应过来它只是在严格执行锁文件。那次之后我长了个记性如果你发现自己处在一个已经由 uv 管理的项目里想安装任何包第一反应应该是uv add而不是uv pip install。哪怕只是临时加个小工具也先 add 一下不想留痕再用uv remove清理。6.2 CI 和部署脚本里的推荐写法在 CI 里我通常有两种写法。如果是 uv 项目也就是有 pyproject.toml 和 uv.lock用uv sync --frozen--frozen会严格使用锁文件不重新解析速度更快也更能保证和本地一致。如果老项目还在用 requirements.txt那在 CI 里就先激活环境再用uv pip install -r requirements.txt这两种写法不要混。因为uv sync --frozen之后你再跑uv pip install那个包装了也白装下一轮只要重新 sync 就会被清掉。6.3 什么情况下我会主动退回 uv pip install虽然在项目里我强烈推荐uv add但以下几种场景我会明确选择uv pip install给 conda 环境补包装不想干涉 conda 自己的项目结构在临时容器里装调试用工具容器销毁后不留痕维护老项目临时修复一个线上 bug不想顺手重构整个依赖管理方式用 uv 作为 pip 的平替只想享受速度红利不想引入新概念。在这些场景里uv pip install是效率工具一旦长期项目开始稳定维护它的定位就应该让位给uv add、uv lock、uv sync这条完整链路。6.4 一些上手 uv 时的实用建议最后分享几个我在实际使用中摸索出来的小细节。执行uv add之前先确认当前目录确实是项目根目录否则容易把 pyproject.toml 建错地方新建环境时直接让 uv 管理 Python 版本它自己会下载解释器不用先在系统里装 PythonWindows 上如果遇到无法加载命令的报错检查一下 PowerShell 执行策略和 PATH内网环境下载 uv 时尽量选择和目标机器 CPU 架构匹配的二进制包。我自己实际上手 uv 只花了一个下午真正理解uv add和uv pip install的区别却花了将近一周。区别不在于命令拼写而在于你把自己定位成用一个快一点的 pip还是用一个现代化的 Python 项目管理器。对我个人来说前者适合临时场景后者才适合所有想长期维护的项目。如果你正在纠结要不要切换我的建议是拿一个新项目试一遍uv init加uv add的流程跑两个星期再回头看你会觉得之前的纠结完全值得。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。