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等等)
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静态检查, 提升代码的健壮性
- 打包、依赖管理、部署相对复杂
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更健壮。
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: 虚拟环境目录
使用示例:
4.2 Python管理
通过`uv python`命令来管理python版本, 二级子命令功能有list、install、find、pin、dir、uninstall。
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,可实现脚本的复制即可运行,不要再额外担忧环境不同导致代码不能正常运行。
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的主要贡献(将这部分流程自动化)保证代码的质量-
uv lock: 解析pyproject.toml生成精确版本依赖元数据 uv.lock -
uv venv: 创建.venv虚拟环境 -
uv sync: 确保环境和uv.lock一致 -
source.venv/bin/activate#: 进入虚拟环境 -
python example.py: 运行
-
-
uv tree查看依赖 -
uv help查看帮助 -
uv self管理uv -
uv cache缓存管理 -
uv export将uv.lock导出为其他格式 -
uv tool执行或安装python提供的命令行工具
-
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__.pyworkspace配置示例:
- 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.