Chapter 05

Python 版本管理

告别 pyenv、告别 "brew install python@3.12"——uv 把多 Python 版本切换变成一条命令的事

为什么不用系统 Python?

macOS、Linux、Windows 都自带"系统 Python",但直接用它开发是大坑:

过去的解法是 pyenv:下载源码本地编译多版本 Python。但编译很慢(10-20 分钟)、经常因为缺失系统库(openssl、readline)失败。

uv 的解法:python-build-standalone

uv 不从源码编译,而是下载 python-build-standalone 项目预编译好的可重分发 Python 二进制(由 Gregory Szorc 维护,也是 Astral 内部采用的方案)。一键下载、零依赖、秒级完成。

uv python 命令全家福

# 列出所有已安装的 Python
uv python list

# 列出所有可下载的版本
uv python list --all-versions

# 安装某个版本
uv python install 3.13
uv python install 3.12.4           # 精确到补丁
uv python install 3.11 3.12 3.13   # 一次装多个
uv python install ">=3.12,<3.14"      # 用约束装

# 安装 Free-Threaded(无 GIL)版本(PEP 703)
uv python install 3.13t

# 升级(Python 有安全补丁时)
uv python upgrade 3.13

# 卸载
uv python uninstall 3.11

# 查找:某版本的解释器在哪里
uv python find 3.12
uv python find ">=3.12"

# 在项目中固定版本(生成 .python-version)
uv python pin 3.13
cat .python-version   # 3.13

.python-version 文件

这是一个单行文本文件,内容就是 Python 版本号(如 3.13)。放在项目根目录,所有 uv 命令会自动选择该版本。必须提交到 git,这样队友和 CI 也用同一个版本。

$ cat .python-version
3.13

$ uv run python --version
Python 3.13.1
# uv 自动检测 .python-version,没装会自动下载

格式兼容 pyenv,因此从 pyenv 迁移零成本。支持多行(表示优先级):

3.13
3.12
# uv 优先用 3.13,没有就用 3.12

requires-python 与 .python-version 的关系

requires-python (pyproject).python-version
作用声明项目兼容哪些 Python本地开发默认用哪个
例子">=3.12" 表示 3.12 及以上都行3.13 表示本地用 3.13
是否提交是(pyproject.toml 是源)是(团队一致性)
必须一致吗.python-version 必须满足 requires-python;不一致 uv 会警告

python-preference — 用系统的还是 uv 管的?

[tool.uv]
python-preference = "managed"
# managed      —— 优先用 uv 装的(默认)
# only-managed —— 只用 uv 装的,忽略系统
# system       —— 优先用系统的
# only-system  —— 只用系统的(不让 uv 下)

团队统一规范推荐 only-managed——避免个别人用 brew 装的 Python 出现"我这里 3.12.2 你那里 3.12.5"。

Python 安装目录

uv 下载的 Python 存放位置:

$ ls ~/.local/share/uv/python/
cpython-3.11.9-linux-x86_64-gnu/
cpython-3.12.4-linux-x86_64-gnu/
cpython-3.13.1-linux-x86_64-gnu/
cpython-3.13.1+freethreaded-linux-x86_64-gnu/

Free-Threaded Python(PEP 703)

2024 年 Python 3.13 实验性引入"无 GIL"构建版本,2025-2026 年逐步成熟。uv 原生支持:

# 安装无 GIL 版
uv python install 3.13t

# 创建使用无 GIL Python 的项目
uv init --python 3.13t myparallel
cd myparallel
uv run python -c "import sys; print(sys._is_gil_enabled())"
# False —— GIL 关闭
Free-Threaded 注意事项

① 很多 C 扩展还没适配无 GIL(装 numpy 可能退化为兼容模式)。② 单线程性能约慢 5-10%(为了线程安全牺牲)。③ 主要受益场景:CPU 密集的纯 Python 多线程(机器学习推理前处理、爬虫等)。生产使用建议先充分测试。

Python 版本切换的常见场景

场景 1:新项目用最新版

uv init myapp
uv python pin 3.13
uv add fastapi

场景 2:旧项目用老版本

cd legacy-project  # 发现 .python-version = 3.9
uv sync
# uv 自动下载 Python 3.9,创建对应的 .venv

场景 3:临时跑一个脚本

# 在 3.12 环境下跑 main.py,即使项目 pin 的是 3.13
uv run --python 3.12 main.py

场景 4:CI 矩阵测试多版本

# .github/workflows/test.yml
strategy:
  matrix:
    python: ["3.11", "3.12", "3.13"]
steps:
  - uses: astral-sh/setup-uv@v3
  - run: uv python install ${{ matrix.python }}
  - run: uv sync --python ${{ matrix.python }}
  - run: uv run pytest

PyPy / GraalPy 支持

uv 也可以装替代实现的 Python:

uv python install pypy@3.10
uv python install graalpy@24

# 创建项目时指定实现
uv init --python pypy@3.10 myapp

关闭 uv 的 Python 管理(公司禁用自动下载)

某些企业合规环境不允许工具从互联网下载解释器,可以禁用:

export UV_PYTHON_DOWNLOADS=never
# 或在 pyproject.toml 中
[tool.uv]
python-downloads = "never"
python-preference = "only-system"
本章小结

uv python install 一条命令解决过去 pyenv + 编译的整套烦恼。记住 .python-version(本地固定)和 requires-python(项目兼容范围)的分工。生产/团队推荐 python-preference = "only-managed" 保证一致性。下一章进入 venv 和 uv run 的无激活工作流。