(由 DeepSeek 辅助翻译)
(警告,这是一篇长文。我有点写嗨了。)
经过一年时间,在许多客户项目中尝试使用 uv,这个由 Astral 推出的新 Python 项目管理工具,我看到了它的优点和不足。
我的结论是:如果你的情况允许,总是先尝试 uv
。如果不行,再退回到其他工具。
它是帕累托解决方案,因为比起费劲去弄清楚该用什么工具,uv
更容易上手,而且你很少会后悔。实际上,迁移到 uv
和从 uv
迁移回来的成本都很低,但它带来的价值却相当高。
虽然这篇文章会深入探讨为什么如此,但我们也会专门讨论什么时候你不想使用 uv。
不过,这篇文章并不是关于如何使用 uv 的教程。后续会有一篇专门的文章。
尽管我对 uv
充满热情,但我坚持认为,在没有看到它在各种工作场景中的表现之前,我不能轻易推荐它。
这是因为 Python 社区庞大且多样化。有学生、数据科学家、AI 开发者、Web 开发者、系统管理员、生物学家、地理学家、插件作者……他们可能在大学、政府机构、初创公司、军队、实验室或大公司工作。
他们的技能水平、经验、环境和约束各不相同,而工具的普适性越强,我就越能推荐它。
这与 PHP、JS、Java 或 Ruby 的情况非常不同。相对来说,很少有人用 Java 开发 X-plane 插件,用 Ruby 编写 GIS 脚本,用 JS 编写银行定价引擎,或用 PHP 作为最新 LLM 模型的主包装。这些语言虽然也能做到,但我看到 Python 的应用范围更广。
因为我是一名自由职业开发者,同时也是一名培训师,我经常在这些领域游走,而且我见过其他工具在这些场景中惨败的情况。pyenv、poetry、pipenv、pdm、pyflow、pipx、anaconda……
事实上,我的博客之所以开始受欢迎,是因为一篇文章:为什么不告诉人们“简单”地使用 pyenv、poetry、pipx 或 anaconda
所以我不想给人们虚假的希望,推荐一些只在我的小圈子里有效的东西,不幸的是,大多数极客都会这么做。
现在,我已经看到了 uv
的使用方式和它可能遇到的问题,我不仅可以告诉你应该使用它,还可以告诉你为什么。
当然,我也会告诉你什么时候不要使用它。
我反复强调过,Python 的引导过程是所有问题的根源。所谓引导,指的是安装 Python 本身,以及配置一个新项目,以便后续安装依赖或构建包。你之后遇到的许多问题(例如打包问题)实际上都源于此。
这是因为:
- Python 有很多不同的安装方式,每种方式都有不同的默认设置和陷阱。而且这些还因操作系统而异。
- 光是安装 Python,就需要了解很多前置知识,而 Python 是一门特别适合初学者的语言,而初学者,顾名思义,并不具备这些知识。
- Python 的应用场景非常广泛,因此很难创建“一个教程适用所有情况”。在公司锁定的 Windows 机器上提供的 Python 体验,与在 Debian 爱好者笔记本电脑上的体验完全不同。
- 很少有人能给出关于这方面的好建议,但每个人和他们的猫都会以权威的语气谈论它。网上关于这方面的废话实在太多了。
- 有很多工具试图解决这个问题,因此我们现在面临着选择悖论。
PATH
、PYTHONPATH
、糟糕的命名约定、在同一台机器上安装多个 Python 版本、Linux 上的可选包,以及 Python 作为系统依赖项,这些都为你提供了无数种搬起石头砸自己脚的方式。-m
和py
在它们的使命中失败了。大多数人甚至不知道它们的存在。- 编译扩展的流行给这一切增加了不少乐趣。
- 人们会遇到直接与这些问题相关的问题,但完全不知道原因,因此他们会直接说“Python 打包太烂了”,因为他们会把问题归咎于他们正在使用的工具,而不是他们根本不知道的根源问题。
因此,一个好的 Python 项目管理工具应该具备以下特性:
- 独立于 Python 的引导过程,因此不会出现鸡生蛋蛋生鸡的问题,同时也能绕过
PATH
和PYTHONPATH
问题。 - 能够在所有平台和情况下以统一的方式安装和运行 Python。
- 在基础工具(
pip
和venv
)与自身之间提供桥梁。 - 拥有非常强大的依赖解析器。
- 让简单的事情变得简单(安装东西),让复杂的事情变得可能(在不同于开发环境的操作系统上安装锁定依赖)。
- 所有这些都要易于安装和使用,当然,还要足够可靠,让你放心地将它用于你技术栈中最重要的部分之一。
我是说,这有什么大不了的?
uv
的愿景非常出色。我这么说并不是随口一说。
这不是偶然,而是由 Astral 团队精心策划的,他们才华横溢且工作努力。
首先,他们让 uv
完全独立于 Python 本身。无论你安装或更新 uv
还是 Python,都不会相互影响。Python 的引导问题、PATH
问题或导入问题都不会以任何方式影响 uv
。
因此,你在安装 uv
时不需要对 Python 生态系统有太多了解。不需要纠结它应该安装在哪里(系统中?venv 中?),也不需要担心新关键字或弃用会如何影响它。
然后,他们首先提供了 pip
和 venv
的接口,这样你就可以在现有项目、工具和范式中使用它。这是 uv
的一个被低估的好处。它不仅使采用变得更容易、更不吓人,还:
- 表明 Astral 尊重现有的社区。
- 承认全球范围内已经存在的庞大遗留代码库的重要性。
- 展示了他们愿意承担开发和维护这一历史遗留问题的决心,并且会持续多年。
对我来说,这传递了一个信号:“我们了解我们的部落,并且我们对此非常认真。”
这也意味着你可以像以前使用 pip
和 venv
一样使用 uv
(甚至可以使用 pip-tools),并且永远不需要学习更多东西。你不需要学习 uv run
、uv add
或 uvx
。光是你在基本任务中获得的可靠性和速度就足以证明迁移的合理性,因为它的工作流程相同,只是更快且 bug 更少。
所以,即使他们只做到这里,uv
仍然是一个净收益。
但当然,他们并没有止步于此。
他们增加了一种安装 Python 的方式:
- 在所有操作系统上以统一的方式安装。
- 不需要管理员权限。
- 独立于系统。
- 安装多个版本时不会冲突。
- 所有版本都包含相同的标准库(是的,tkinter 无处不在!)。
- 包括 Pypy、No-GIL 和 TCO 版本(!)。
- 没有 shim,不需要编译,且默认设置合理。
在写这篇文章的过程中,我用 uv
在几秒钟内安装了“pypy3.8”。我甚至不记得该怎么做了,但 API 和帮助信息非常清晰,我很快就搞定了,然后,我的机器上就有了一个新的 Python:
1 | ❯ uv python list |
它显示“Installed Python 3.8.16 in 2.71s”。2.71 秒!而且我可以在 Mac 或 Windows 上以同样的方式运行它。这简直太棒了。
没有缺少 Tcl、OpenSSL 或 Gzip 的包。没有与其他 Python 来源的冲突。不需要为每个操作系统使用不同的范式。没有缺少命令或配置错误的 PATH
。
这一切之所以能实现,是因为 Astral 充分利用了一个非常有前途的项目 python-build-standalone,并最终接管了它。这些是不需要安装程序的 Python 构建。团队不仅大大改进了这个项目,现在还在积极尝试将这些改进贡献给 cPython 上游。事实上,在整个项目中,他们展示了他们对相邻 FOSS 项目的贡献意愿。
我发誓,我没有收他们的赞助!
当然,他们还为 uv
添加了高级项目管理功能,超越了 pip
和 venv
。这些功能是可选的,因此你可以按自己的节奏逐步采用。
uv init
不仅创建了一个 “.venv”,还默认创建了一个pyproject.toml
、一个 git 仓库(带有 Python 特定的 .gitignore)、一个README.md
和一个hello.py
。当然,这些是可配置的。- 你可以在
pyproject.toml
中声明你的根依赖,或者用uv add
添加它们。 uv remove
实际上会正确清理你的仓库。uv lock --upgrade-package <package>==<version>
让你可以小心地逐个版本升级你的包。uv build
会从你的项目中创建一个.whl
包,但uv
并不要求你的项目必须能够被构建。uv run
会在 venv 中运行任何命令,即使它没有被激活。你甚至不需要知道有一个 venv,或者激活是什么意思。- 所有这些命令都会自动且透明地更新锁文件。你不需要手动管理你的项目。这一切都由
uv
处理。这之所以可能,是因为uv
速度非常快,你甚至不会感觉到更新正在进行。你甚至不需要知道锁文件是什么。 - 锁文件是跨平台的(这本身就是一个疯狂的事实!),所以你可以在 Windows 上开发,然后在 Linux 上部署。
出色的性能(再次强调,这是设计使然,Astral 使用了一些非常有趣的技巧来加速所有操作,参见我们的采访)意味着它不仅会让你感觉毫不费力,还会鼓励你进行实验。你再也不需要为尝试新事物付出代价了。你可以在几秒钟内从头开始。
最后但同样重要的是,工具的可靠性。我数不清 pyenv
、pipenv
或 poetry
在我身上崩溃了多少次,给我留下一堆堆栈跟踪让我处理。这些工具的粉丝会告诉你,这种情况不会发生在他们身上,但首先,他们在撒谎(我见过有人刚说完这话就遇到了问题!),其次,他们通常只在一两种情况下使用它,这让他们对全局的视野非常狭窄。
另一方面,uv
不仅非常稳定,还具备三个特别罕见且令人向往的品质:
- Astral 非常擅长修复 bug。他们倾听反馈。他们对报告反应迅速。而且他们非常勤奋。他们的 bug 追踪器简直令人惊叹。
- 他们有很棒的测试文化。例如,他们有一个非常强大的解析测试套件。而且他们把它做成了一个单独的包,这样其他项目也可以使用它。
- 他们提供了非常优秀的错误信息。看看这个漂亮的解析失败:
1 | ❯ uv add httpie==2 |
你可以说这要归功于 pubgrub,但他们所有的错误信息都力求如此,而且他们选择依赖项时非常谨慎。
基本上,他们采用了 pip
、rye
和 poetry
中有效的东西,并摒弃了所有无效的部分。然后他们花了几个月的时间消灭 bug,将其提升到了一个令人难以置信的质量水平。
这一点不能被低估,因为如此高的质量和奉献精神在软件中极为罕见,我通常只会将其与 VLC 或 sqlite 这样的项目相提并论。这就是我认为 uv
所处的水平。
结果是,当我在培训中将 uv
交给我的学生时,我需要做的工作非常少。我惊讶地看到他们如此轻松地就能上手并开始高效工作,几乎不需要我的干预。这种情况在使用其他工具时从未发生过。
在专业项目中,情况略有不同。新项目可以轻松地从 uv
中受益。遗留项目则是可能出现障碍的地方,我们稍后会看到。
你可能会认为我对这个看起来不过是一个高级包管理器的工具赞不绝口,但我还有一些额外的看法。
在创建 uv
时,Astral 创建了强大、快速且健壮的基元。当你这样做时,你打开了一个全新的用例宇宙。
它确实做到了。
在这个案例中,基元是 Python + 依赖项的供应和隔离。
这听起来没什么大不了的,但它是一个范式转变。在此之前,我认为这些是约束。我必须做的事情,可能会出错,速度慢,而且我必须小心处理,才能进入我感兴趣的部分。
但现在有了 uv
,我将它们视为能力:我可以随意调整我的工作流程。
我发表了一整篇关于 uv 技巧 的文章,但为了说明我的观点,我在这里复制其中两个:
uv run --with jupyter jupyter notebook
会在当前项目中运行 jupyter……而不会将 jupyter 及其依赖项添加到项目中!而且由于uv
的缓存机制,后续调用会非常快。- 想知道
pendulum
在新的 Python no GIL 构建中导入时的行为吗?我刚才运行了uvx --with pendulum -p 3.13t python
。它下载了新的 Python,安装了它,创建了一个临时 venv,安装了pendulum
,然后启动了一个 Python shell。几秒钟内就完成了。然后,我退出,它就消失了。
这完全改变了你的工作方式。我以前有一个巨大的 test
venv,我经常摧毁它。我曾经避免测试一些东西,因为太麻烦了。我曾经避免使用某些工具,或者因为它们太大或不够有用而不得不为使用它们付出代价。诸如此类,不胜枚举。
uv
出乎意料地带来了比 Python 项目管理更多的东西。它添加了 uvx
,一个类似 npx
的 Python 工具,我认为它是“pipx 的正确实现”。但它还添加了对 内联依赖 的支持,这与其他 uv
功能(还记得那些好的基元吗?)相结合,彻底改变了你使用 Python 脚本的方式。
以前,你要么避免在小型 Python 脚本中使用依赖项,要么需要一些繁琐的变通方法才能让它们为你工作。就我个人而言,我曾经管理一个巨大的 venv,专门用于本地脚本,我每年都要清理和重建它。
现在,你可以自由地使用任何东西。它速度快。透明。高效。自描述。
因为所有这些都不是强制性的,也不是摆在面前的,你可以按照自己的节奏发现并采用它们。我敢打赌,随着时间的推移,社区会发现越来越多的组合方式。
我保留了一份 uv
缺点的清单,只是为了这篇文章。但随着 Astral 日复一日地解决他们的 bug 追踪器,这份清单变得越来越短。他们添加了可编辑的安装、uv run
的 Python 回退、tkinter 无处不在、支持非打包项目、尊重 XDG、提供了头文件(没错!)等等。他们甚至在开发 任务支持,你读到这篇文章时他们可能还在做。
所以现在已经没有什么可抱怨的了,但我必须提一下。
讽刺的是,uv
无法解决真正的打包问题,而不是引导问题的后果。比如糟糕的版本标记、缺少 wheel、名称冲突等。这是因为这些超出了 uv
的控制范围,而是 Pypi 上可用数据质量的固有问题。你之所以会在使用 uv
时看到更少的打包问题,仅仅是因为它把其他所有事情都做对了。
因此,我不会在这一点上评判 uv
,这非常有趣,毕竟它是一个包管理器。uv
在它现有的基础上表现得非常好。
然而,因为它有一个更好的解析器,它实际上可能会在遗留项目中破坏你的 venv,尤其是当你使用旧版本的 pip
时,pip
的依赖解析更为宽松。
我有一个朋友决定不使用 uv
,因为他第一次使用它时,是在一个刚刚迁移到 Python 3 的 15 年老代码库上。这个代码库建立在从未清理过的 pip freeze
导出依赖上,uv
无法让它正常工作。
另一个问题是,由于 uv
使用了 python-build-standalone
,你只能使用为该格式构建的 Python 版本。虽然你可以通过 python.org 的安装程序、deadsnake 或 pyenv 安装更多版本的 Python。对于新项目来说,这似乎不是问题,但对于一个已经运行了很长时间并且需要特定 Python 版本的项目来说,这可能是个问题。幸运的是,uv
并不介意与外部安装的 Python 版本一起工作,所以这并不是什么大问题,但有些人可能没有意识到这一点。
如果你想用更快的 Python 替换提供的版本,这也是一个重要特性。python-build-standalone 的可执行文件本身会稍微慢一点(我刚运行了 pyperformance 基准测试,uv
的 3.10 比我的 Ubuntu 版本慢 3%),而且你可能有一天会想使用为你的硬件优化的 Python 版本。虽然这不是常见需求,但拥有这个选项总是好的。
是的,我现在是在吹毛求疵了。
还有一个问题是 uv
的缓存占用了多少空间。使用一年后,它在我的磁盘上占用了超过 20GB。你可以用 uv cache clean
删除它,但这样你就会失去它提供的惊人速度优势。
再说一次,这并不是一个严重的问题。我有 2TB 的硬盘。而且,uv
占用的空间很可能比我之前所有 venv 加起来还要少,因为与 pip
不同,包是硬链接的,只占用一次空间。
我现在有一个小问题,那就是 $UV_PYTHON
强制使用某个 Python 版本,而不是给你一个默认版本,但这个问题已经解决了。
显然,我还必须提到房间里的大象:uv
是商业公司 Astral 的产品。尽管它是开源的,而且无论 Astral 做得多么出色,你都必须相信他们会为社区保持它的可用性和更新。更重要的是,他们目前还没有盈利,我们还没有看到他们的商业产品,所以我们不知道未来会发生什么。有些人,比如我们在采访 Russell Keith-Magee 时提到的,对此感到紧张,并认为在将如此重要的部分交给他们之前,我们应该谨慎行事。
我个人并不担心这一点。在我所做的几乎所有项目中,迁移到 uv
都很容易,而且从 uv
迁移回来也不难。虽然会因为失去那些很棒的功能而感到痛苦,但并不难。此外,Astral 通过他们出色的行为积累了大量的信任,所以如果我必须信任某个实体,我宁愿信任他们。事实上,我欢迎他们推出付费产品,我想给他们钱。我希望他们蓬勃发展。
你还想让他们做什么来赢得你的信任?在你奶奶在圣诞晚餐上噎住时给她做 CPR 吗?他们已经做得非常好了。我不觉得我有资格要求他们展示更多的诚意。
它是开源的,任何人都可以 fork 它。更不用说代码非常干净。当然,它是用 Rust 写的,但现在有很多 Python 开发者懂 Rust。我敢肯定,如果 Charlie 被公交车撞了(抱歉伙计,我并不希望这样,但公交车是无情的生物),Armin 会接手,或者其他人也会。
不,目前使用 uv
的最大限制是企业采用。在大型、安全、锁定的企业环境中,安装新的依赖项非常困难。现在,如果你的 IT 安全部门控制了你机器上能做和不能做的事情,他们不会让你安装 uv
。除非它达到稳定版本并满足很多要求。
不过,我假设这就是 Astral 赚钱的方式,通过成为 Anaconda 的直接竞争对手。我向你保证,市场对此有需求,因为 Anaconda 与蝙蝠侠相反,如果他们能处理好游说部分(这非常难,别误会),技术方面已经准备好为 uv
唱赞歌了。
如果他们想这么做,还必须解决另一个问题:有相当一部分 Python 程序员不习惯使用命令行。尤其是在 Windows 上,也就是大部分企业市场。这就是为什么 Anaconda 有一个 GUI。这也是我推荐 python.org 安装程序的原因之一。对于完全初学者来说,要求使用 CLI 工具是一个入门障碍。
最后,uvx
(以及 uv tool install
)与 pipx
有类似的问题,它鼓励你在项目之外安装一些工具。这对于像 yt-dlp 或 httpie 这样自包含的独立工具来说是有意义的。但对于关心语法或库的开发工具来说,这是一个陷阱,比如 mypy
会安装在某个 Python 版本中,但随后会用在另一个可能不兼容的 Python 版本的项目上。它们会崩溃得很厉害,而许多用户不会理解为什么。
如你所见,现在已经没有真正的阻碍了,所有这些都是小问题。我们已经过了我可以指着某样东西说“这就是你绝对不应该使用 uv
的原因”的阶段。
基本上,有 5 种情况你不应该使用 uv
:
- 你有一个遗留项目,使用
uv
解析依赖项会失败,而你无法(或不想)为了迁移而清理这个烂摊子。 - 你处于一个不允许你使用它的企业环境中。
- 你还不信任它,因为它还不是稳定版本,因为 Astral 还没有发布他们的商业产品,因为 Rust 的贡献者池太小,等等。
- 你需要一个
uv
不提供的特定 Python 版本,而且你不想使用uv
,如果你不能用它安装 Python,尽管它与第三方安装的 Python 配合得很好。 - 你认为 CLI 对团队来说是一个太大的障碍。
对我来说,3 和 4 并不是技术问题,所以它们更像是选择而不是阻碍。我不是来说服你做出不同的选择的,我在这场比赛中没有马,你做你自己就好。
第 2 点是你无法改变的,所以这一点没有讨论的必要。
这意味着我真的只需要考虑第 1 和第 5 种情况,对此我有一个简单的建议:
总是先尝试 uv
。如果它不工作(这种情况非常罕见),就回到你之前的方式或找到一个变通方案。
如果 CLI 被证明是一个太大的问题,建议使用 python.org 安装程序来提供 Python,并使用一个抽象掉 uv
的 IDE 插件。但先试试,能编程的人通常都能学会足够的命令行基础知识来使用 uv
。
如果真的不行,那就换其他工具。
考虑到使用该工具的巨大好处、非常低的采用成本,以及它不适合你的机会非常有限(无论是第 1 种、第 5 种情况,还是我不知道的其他边缘情况),这只是一个不错的赌注。
从统计上讲,你大多数时候都会赢,这就足够了。
在 v1 版本之前,仍然有一些差距,这是企业未来的要求,因为你不能在那里频繁更新。我假设某种形式的打包功能会被添加到工具中,作为 pex/shiv 的替代品,可能还会有一个构建后端。我不知道他们是否有计划允许为你的应用程序创建安装程序,但这将是合乎逻辑的结论,尽管这比看起来要复杂得多(光是签名就很难做好)。
我疯狂地运行 uv self update
来获取他们不断推出的新功能,但说实话,一旦他们完善了任务支持,这个工具对我来说就已经功能完备了。
无论如何,我打算编辑所有关于 pip
和 venv
的文章,提到 uv
。并且写一篇 uv
的教程。
如果 Python 是你的工作,你还是应该学习如何使用 pip
和 venv
,因为你可能有一天会处于 uv
不可用的情况。
尽管如此,从现在开始,我会告诉所有人“直接用 uv
”。
它是帕累托解决方案,你知道我有多爱帕累托。
_编辑:一些读者在评论部分指出了其他有趣的阻碍。我没有遇到过,所以如果你想要更多,请往下滚动。HN 讨论也很有趣。