Python终极构建指南: uv

1 概述

人生苦短,我用Python,享受美好生活。

Python能让万物为我所用,但:

  • Python的第三方库不兼容、部署运维复杂
  • Python中的五花八门的工具,让人眼花缭乱。如何选择管理工具: pyenv/conda env/virtualenv/poetry? 该如何选择安装工具: pip/pip-tools/pipx? 该如何选择代码检查工具: ruff/pylint/black?

使用Rust编写的uv横空出世,使用uv管理python的一切,让开发更加高效、稳定。

这才是生活。

2 发展中的构建工具

所有的程序语言,都绕不开“构建部署” 这一环节。回顾40年的历史,2025年今天的构建最优解就以Rust语言的Cargo为代表的cargo工具。

在python即uv!

2.1 C++

1983年,C++诞生于贝尔实验室,作为面向对象语言,并且凭借着接近C语言的效率,在工业界使用的开发语言中占据了相当大份额。

  • 优点: 高性能、面向对象
  • 缺点:复杂、无统一的构建工具,每个大厂都有自己内部的工具(如bazel/CMake等等)
cpp_bjarnestroustr.jpg

Figure 1: c++发明人

2.2 Perl

1987年, 通用脚本语言Perl发布,曾被称为脚本语言中的瑞士军刀。中心思想:There's More Than One Way To Do It. (不只一种方法来做一件事。)

  • 优点:基本无
  • 缺点:语法冗余导致可以写得很随意但难以阅读(1月后看不懂自己写的代码)

2.3 Python

2000年,解释性编程语言Python 2.0发布,设计哲学:强调代码的可读性和简洁的语法。人生苦短,我用Python。

  • 优点:开发效率高、庞大的生态
  • 缺点:

    • 性能差(计算慢、内存高): 底层库用C++/Rust编写
    • 动态类型导致的运行时错误:用mypy静态检查, 提升代码的健壮性
    • 打包、依赖管理、部署相对复杂
python_guido_van_rossum_in_pyconus24.jpg

Figure 2: python发明人

2.4 Rust

2015年,编译型编程语言Rust 1.0发布,设计哲学:安全、并发、实用。

  • 优点:高性能、安全、现代化构建工具
  • 缺点:学习成本高,但值得学习

2.5 今天的选型

简单回顾完40年左右的程序语言历史,再说2025年的今天,个人如何程序选型?

  • 必须掌握一门底层编译型语言以保证性能。考虑到Java/Go有GC性能天然差一些,C++构建工具五花八门,我选Rust。
  • 必须掌握Python,因为人生苦短,要留下时间去享受生活

总之,底层用Rust写保证性能,对外包一层Python扩大影响力。

Python尽管被广泛应用,但应用于生产有两类主要问题:一是庞大的第三方库(万物皆为我所用)导致构建相对复杂(虽然有anaconda/pip等工具),二是动态类型导致代码的健壮性很差。

2024年用Rust编写的python构建工具uv横空出世,一年内github star数目稳定线性增长快接近6万,个人认为是当前(预计未来N年内)Python最好的现代化构建工具,使python的构建更简单;同时,搭配mypy也能让python更健壮。

star-history-202567.png

Figure 3: uv github star 趋势

3 uv设计理念及特性

什么是uv?

An extremely fast Python package and project manager, written in Rust.

uv是由Atral团队(ruff工具开发者)设计、使用rust编写的、一个极快的Python构建工具,用于python项目管理。

3.1 uv设计理念

uv的背后核心设计理念,借鉴自Rust语言的cargo,本质是:极速驱动效率、以 统一简化生态、以开发者为中心重塑体验。通过高性能、统一接口、人性化设计, 将python由一个“低信心体验”的开发环境,转为一个专业、高信心的开发环境。

极致性能优化:

  • 由Rust编写,通过并行依赖解析、全局缓存复用,包安装速度及虚拟环境创建比已有的工具(pip/venv)快10-100倍
  • uv是一个静态的二进制文件,大小42MB,无Python运行时依赖,启动快,适合容器化环境(比14G左右的anconda瘦身太多)

统一简化的工具链:将“pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv等诸多工具的功能”整合为一个单一的二进制,减少生态碎片化。

开发者为中心:

  • 对于大型项目:通过声明式配置、动态按需创建虚拟环境做到动态环境的隔离
  • 对于简单脚本:通过支持内联依赖声明、脚本自带依赖元数据,实现脚本的复制粘贴即运行。

3.2 uv特性

  • 极速驱动效率:⚡️ 速度比pip快10-100倍、💾磁盘空间通过全局缓存及硬链接来节省
  • 统一简化生态:🚀 单一的uv工具,可替换掉pip/pip-tools/pips/poetry/pyenv/twine/virtualenv等五花八门的碎片化工具
  • 开发体验:

    • ❇️ 一键安装、一键配置、一键升级、一键卸载,绿色干净透明

      curl -LsSf https://astral.sh/uv/install.sh | sh # 安装
       echo 'eval "$(uv generate-shell-completion bash)"' >> ~/.bashrc # 配置
       uv self update  # 升级
       rm ~/.local/bin/uv ~/.local/bin/uvx # 卸载
    • 🐍 支持多个python版本、跨平台的依赖管理、通过工作区支持大型项目、构建发布python代码
    • 🗂️ 提供全面的项目管理,Python由玩具升级为专业的生产工具

4 uv使用

4.1 开发流程概述

使用uv开发项目的流程,主要为python版本及依赖(简单脚本、复杂项目)的管理、开发测试、构建发布。

几个配置文件需要重点关注,后续会反复用到。

  • pyproject.toml: 项目的元数据文件,含项目的依赖关系、Python的版本
  • uv.lock: uv解析pyproject.toml来生成uv.lock, 记录精确版本的三方依赖关系,目标是可复现 (不要手动修改)
  • .python-version: uv用来管理python的版本 (不要手动修改)
  • .venv: 虚拟环境目录
uv_usage_arch.svg

Figure 4: UV 开发流程
uv_usage_arch.svg

Figure 5:

使用示例:

uv_demo_project.gif

Figure 6: UV project 示例

4.2 Python管理

通过`uv python`命令来管理python版本, 二级子命令功能有list、install、find、pin、dir、uninstall。

uv_manage_python.gif

Figure 7: uv python 示例

4.3 依赖管理

使用uv之前,我更多的是人工手动维护一个长期的python虚拟开发环境,导致依赖管理杂乱且不准。

  • 杂乱:半年之后都忘记何时手动添加过哪些三方依赖,依赖无明确记录
  • 不准:版本不精准导致代码不能复现:半年前通过pip安装某第三方的依赖,当前时刻安装不再能运行。(第三方包更新快且可能版本间不兼容,`pip install xxpackage`在半年前和此刻运行安装的是不同的版本)

对于生产环境的使用,使用conda的yaml文件维护一个虚拟环境,如果需要保证100%复现,需要手动明确各个库的精确版本。

uv的设计理念,抛弃长期人工维护的虚拟环境,自动根据文件内声明的依赖、按需创建/更新一个虚拟环境:

  • 通过“pyproject.toml”或“myscript.py”中的内联依赖元数据,描述依赖关系
  • 通过uv.lock精确控制三方依赖保证代码可复现

4.3.1 简单脚本

一个可执行python脚本:

  • 单一文件,内置元数据格式允许在脚本文件本身内声明该脚本依赖
  • uv run main.py: 自动根据文件内声明的依赖、临时按需创建一个虚拟环境以用于运行script

基于uv,可实现脚本的复制即可运行,不要再额外担忧环境不同导致代码不能正常运行。

uv_script_demo.gif

Figure 8: uv 简单脚本示例

4.3.2 复杂项目

使用uv开发python,本质上是用uv的十几个子命令来运行:

  • 初始化项目: uv init
  • 管理依赖:

    • uv python 管理python依赖, 和.python-version文件关联
    • uv add/remove 管理三方依赖,和pyproject.toml关联
    • uv versoin 用来管理项目的版本, 和pyproject.toml关联
  • 开发测试:

    • uv run example.py : 理解这个才能理解uv的主要贡献(将这部分流程自动化)保证代码的质量

      1. uv lock: 解析pyproject.toml生成精确版本依赖元数据 uv.lock
      2. uv venv: 创建.venv虚拟环境
      3. uv sync: 确保环境和uv.lock一致
      4. source .venv/bin/activate#: 进入虚拟环境
      5. python example.py: 运行
    • uv tree 查看依赖
    • uv help 查看帮助
    • uv self 管理uv
    • uv cache 缓存管理
    • uv export 将uv.lock导出为其他格式
    • uv tool 执行或安装python提供的命令行工具
uv_command.svg

Figure 9: uv命令和项目开发流程
uv_command.png

Figure 10:

4.3.3 工作空间(workspace)

超大型项目的代码,如何管理?

切分成多个package, 每个package有独立的pyproject.toml。

  • 多个package如果有相同的依赖,通过workspace管理:通过共享一个uv.lock/.venv统一管理,保证workspace(N(N>=1)的package,统一管理)中各个package依赖的统一性。
  • 多个package如果无相同的依赖(有冲突),通过uv.source.paths中的path管理:

    • 每个package有各自独立的uv.lock/.venv(更加细粒度的依赖管理的控制和虚拟环境的控制)
    • 如果root和package 出现冲突,如何处理? 比如某个package太旧,不支持新版本的python, 可能需要单独一个uv.lock, 通过命令行工具或http服务为另外一个package使用

workspace目录示例:

myapp/                                           <-- workspace root
├── packages
│   ├── lib1                                     <-- workspace member
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── src
│   │       └── lib1
│   │           ├── __init__.py
│   │           └── py.typed
│   └── lib2                                    <-- workspace member
│       ├── main.py
│       ├── pyproject.toml
│       └── README.md
├── pyproject.toml
├── README.md
└── src
    └── myapp
        └── __init__.py

workspace配置示例:

  • tool.uv.workspace:定义非root的workspace member
  • tool.uv.sources: workspace root中定义,workspace member也生效
[project]
name = "myapp"
version = "0.1.0"
dependencies = [
    "lib1",
    "lib2",
]

[tool.uv.workspace]
members = [
"packages/lib1",
"packages/lib2",
]

[tool.uv.sources]
lib1 = { workspace = true } # dependency lib1 from workspace
lib2 = { workspace = true }

4.4 开发测试实践

作为开发者,开发或测试中的依赖项(如代码静态类型检查器),建议安装到当前 环境的dev分组,既能保证整体环境的一致性,又能保证“使用者”不会依赖这 些开发相关的依赖。

通过代码分析(linter)及静态类型检查提升代码质量:

  • 运行mypy/ruff中,通常需要依赖当前包的安装,建议将mypy/ruff安装到当前环境依赖(但group=dev, 保证发布不会依赖)

    • 将xx安装到dev group: uv add --dev xxx
    • uv run 默认含dev group依赖,但是不会发布(pip install mypackage不会安装相关依赖)
  • 否则: (mypy需要, 否则mypy执行时找不到依赖包)

    • uv tool run mypy
    • uv tool install --with cologlog mypy
    • 坑:uvx(uv tool run) 中的依赖colorlog和 uv run(.venv)中的colorlog物理上是两个包,可能有坑

使用rich替代tqdm

关于editable dependencies:

  • uv build 生成 xx.whl, pip install xx, eidt package/xx.py, env中的xx package outdated
  • Editable installations solve this problem by adding a link to the project within the virtual environment (a .pth file), which instructs the interpreter to include the source files directly.
  • uv uses editable installation for workspace packages by default.

5 参考文献