多版本共存方案
在现代 Python 开发中,同一台机器上往往需要同时存在多个 Python 版本:旧项目依赖 3.9,新项目使用 3.12,还要验证 3.13 的新特性。本节将系统讲解 Windows 的 py launcher、macOS/Linux 的 pyenv、以及 uv 三种多版本共存方案,并给出选择建议与切换技巧。
前置知识
阅读本节前,建议先了解:环境变量配置
基础概念
多版本共存(Multiple Version Coexistence)的核心是:让每个版本拥有独立的安装目录与可执行文件名,再通过启动器或路径切换决定「当前使用哪个版本」。
| 概念 | 说明 |
|---|---|
| 版本启动器(Launcher) | 如 Windows 的 py、pyenv 的 shim,负责按规则选择解释器 |
| Shim | 代理可执行文件,拦截调用并转发到真实解释器 |
| 符号链接(Symbolic Link) | 指向实际解释器的快捷引用 |
| 全局版本 | 未指定项目时的默认版本 |
| 局部版本 | 某目录(项目)内生效的版本 |
| 虚拟环境(Virtual Environment) | 与解释器版本解耦的依赖隔离层 |
三层隔离关系需要区分清楚:
解释器版本(pyenv / launcher 管理)
└── 项目依赖(venv / virtualenv 管理)
└── 依赖清单(requirements.txt / pyproject.toml 固定)版本管理与虚拟环境不是一回事
pyenv 决定「用哪个 Python 解释器」,venv 决定「用哪一套 site-packages」。二者互补,不可互相替代。典型做法是:pyenv 切换解释器版本 → 在该版本下创建 venv → 在 venv 内安装依赖。
语法与用法
Windows:py launcher
# 列出所有已注册版本
py --list
# 使用指定版本
py -3.12 script.py
py -3.13 -m pip install requests
# 使用默认(最高)版本
py script.py
# 查看某版本路径
py -3.12 -c "import sys; print(sys.executable)"macOS / Linux:pyenv
# 安装多个版本
pyenv install 3.11.9
pyenv install 3.12.7
pyenv install 3.13.0
# 查看已安装
pyenv versions
# 设置全局版本
pyenv global 3.12.7
# 设置项目局部版本
cd ~/projects/legacy && pyenv local 3.11.9
# 查看当前生效版本
pyenv version跨平台:uv
# 安装多个 Python
uv python install 3.11 3.12 3.13
# 列出已管理版本
uv python list
# 固定项目版本
uv python pin 3.12.7详细说明
方案一:Windows py launcher
Windows 官方安装程序会自动安装 py.exe(Py Launcher),位于 C:\Windows\py.exe。它会扫描注册表与 PATH 中所有已安装的 Python。
# 完整版本列表(含 32/64 位)
py --list
# 输出示例:
# -V:3.13 * Python 3.13 (64-bit)
# -V:3.12 Python 3.12 (64-bit)
# -V:3.11 Python 3.11 (64-bit)
# -V:3.10-32 Python 3.10 (32-bit)在脚本中声明所需版本,有两种方式:
# 方式一:shebang 行(脚本首行)
#! python3.12
"""shebang.py —— py launcher 会读取首行决定版本。"""
import sys
print(f"当前版本:{sys.version.split()[0]}")# 方式二:py.ini 配置文件(位于 %LOCALAPPDATA%\py.ini)
[defaults]
python=3.12
python3=3.12; 也可以为特定脚本路径指定版本
[commands]
C:\scripts\legacy_tool.py=3.9用 py launcher 创建虚拟环境
py -3.12 -m venv .venv 会用 3.12 创建虚拟环境,是 Windows 上最简洁的多版本 + 隔离方案。
方案二:pyenv(macOS / Linux)
pyenv 通过 shim 机制工作:在 ~/.pyenv/shims 目录中放置大量代理可执行文件(python、pip 等),这些 shim 被加入 PATH 最前面;调用时 shim 根据当前目录与规则决定真实的解释器路径。
版本选择优先级(从高到低):
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1 | PYENV_VERSION 环境变量 | 临时指定,最高优先 |
| 2 | .python-version(当前目录) | pyenv local 生成 |
| 3 | .python-version(父目录逐级向上查找) | 向上递归 |
| 4 | ~/.pyenv/version | pyenv global 生成 |
| 5 | system | 系统 Python |
# 临时覆盖(仅当前命令)
PYENV_VERSION=3.11.9 python --version
# 生成 .python-version
cd ~/projects/demo
pyenv local 3.12.7
cat .python-version
# 输出:3.12.7
# 设置全局默认
pyenv global 3.12.7
# 恢复为系统 Python
pyenv global system
# 查看某个版本的实际路径
pyenv which python~/.zshrc 或 ~/.bashrc 中的必要配置:
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"pyenv 的 shim 依赖 PATH 顺序
eval "$(pyenv init -)" 会把 ~/.pyenv/shims 放到 PATH 最前。若之后又追加了其他 Python 目录到 PATH 前面,shim 会被绕过,导致 pyenv global 看似无效。排查时用 which python 确认是否命中 ~/.pyenv/shims/python。
方案三:uv(现代一体化方案)
uv 用 Rust 实现,同时管理 Python 版本与项目依赖,性能远超 pyenv。
# 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# 或
brew install uv
# 下载多个 Python(存放在 ~/.local/share/uv/python)
uv python install 3.11 3.12 3.13
# 列出
uv python list
# 固定项目版本,生成 .python-version
cd ~/projects/demo
uv python pin 3.12.7
# 创建虚拟环境(自动使用 pinned 版本)
uv venv
# 运行脚本(自动选择正确解释器)
uv run python -c "import sys; print(sys.version)"uv 管理的 Python 存储路径:
~/.local/share/uv/python/
├── cpython-3.11.9-macos-aarch64-none/
├── cpython-3.12.7-macos-aarch64-none/
└── cpython-3.13.0-macos-aarch64-none/方案对比
| 维度 | py launcher | pyenv | uv |
|---|---|---|---|
| 平台 | 仅 Windows | macOS / Linux(Windows 有限支持) | 全平台 |
| 实现 | C++ 启动器 | Shell + shim | Rust |
| 管理对象 | 已安装的 Python | 编译安装的 Python | 下载/管理的 Python + 依赖 |
| 切换粒度 | 命令行参数 | 全局 / 目录 / 环境变量 | 项目 |
| 安装速度 | 不适用(不自带安装) | 慢(需本地编译) | 极快(下载预编译) |
| 是否管理依赖 | 否 | 否 | 是 |
| 学习成本 | 低 | 中 | 低 |
实战示例
案例一:Windows 上三版本共存并各建虚拟环境
# 1. 安装三个版本
winget install --id Python.Python.3.11 -e
winget install --id Python.Python.3.12 -e
winget install --id Python.Python.3.13 -e
# 2. 确认注册情况
py --list
# -V:3.13 * Python 3.13 (64-bit)
# -V:3.12 Python 3.12 (64-bit)
# -V:3.11 Python 3.11 (64-bit)
# 3. 为不同项目创建虚拟环境
mkdir D:\projects\legacy, D:\projects\modern
cd D:\projects\legacy
py -3.11 -m venv .venv
cd D:\projects\modern
py -3.13 -m venv .venv
# 4. 验证 legacy 项目
cd D:\projects\legacy
.\.venv\Scripts\Activate.ps1
python --version # Python 3.11.x
deactivate
# 5. 验证 modern 项目
cd D:\projects\modern
.\.venv\Scripts\Activate.ps1
python --version # Python 3.13.x
deactivate案例二:macOS 用 pyenv 管理并配合 venv
#!/bin/bash
# setup-multi-python.sh —— macOS 多版本环境搭建
set -euo pipefail
# 1. 安装 pyenv
brew install pyenv
# 2. 配置 shell
grep -q 'pyenv init' ~/.zshrc || cat >> ~/.zshrc <<'EOF'
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
EOF
source ~/.zshrc
# 3. 安装三个版本
for v in 3.11.9 3.12.7 3.13.0; do
pyenv install -s "$v"
done
# 4. 全局默认 3.12.7
pyenv global 3.12.7
# 5. 旧项目固定 3.11.9
mkdir -p ~/projects/legacy && cd ~/projects/legacy
pyenv local 3.11.9
python -m venv .venv
source .venv/bin/activate
python --version # Python 3.11.9
deactivate
# 6. 新项目固定 3.13.0
mkdir -p ~/projects/modern && cd ~/projects/modern
pyenv local 3.13.0
python -m venv .venv
source .venv/bin/activate
python --version # Python 3.13.0
deactivate
echo "多版本环境就绪"
pyenv versions案例三:Linux 服务器上用 update-alternatives 切换
# 假设已编译安装了 3.11.9 与 3.12.7
# 注册候选版本
sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.11 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.12 2
# 交互式选择
sudo update-alternatives --config python3
# 验证
python3 --version
# 恢复为发行版默认
sudo update-alternatives --auto python3不要在服务器上随意切换系统 python3
apt、yum、cloud-init 等工具依赖特定 Python。若必须切换,请先确认这些工具在当前版本下仍能运行,或改用 pyenv/venv 隔离。
案例四:CI 中矩阵测试多版本
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: 设置 Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: 安装依赖
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: 运行测试
run: pytest -qCI 中无需 pyenv
GitHub Actions 的 actions/setup-python 已内置多版本切换能力,直接声明 python-version 即可,不必在 CI 中使用 pyenv。
注意事项
- 优先使用启动器而非改 PATH:
py -3.12、pyenv local、uv run都比手工改 PATH 可靠。 - Windows 注意应用执行别名:
python.exe别名会绕过 py launcher,需在设置中关闭。 - pyenv 编译耗时:每个版本需本地编译,可用
pyenv install --list查看源码缓存。 - .python-version 要入库:项目固定版本声明应提交到 Git,保证团队一致。
- 不要删除系统 Python:多版本共存的前提是保留系统版本。
- 注意版本兼容性:不同小版本可能行为差异,见 Python 版本生命周期。
常见问题排查表:
| 现象 | 原因 | 解决 |
|---|---|---|
pyenv global 不生效 | shim 未在 PATH 前面 | 检查 eval "$(pyenv init -)" |
py --list 缺版本 | 未注册到注册表 | 重装该版本并勾选注册 |
| venv 版本不对 | 创建时用错解释器 | 用 py -3.x -m venv 明确指定 |
uv python install 慢 | 网络问题 | 配置 UV_PYTHON_INSTALL_MIRROR |
| 多个 shim 冲突 | 重复安装版本管理器 | 只保留一种方案 |
最佳实践
- 只选一种版本管理方案:Windows 用 py launcher,macOS/Linux 用 pyenv 或 uv,避免多套 shim 冲突。
- 项目内固定版本:提交
.python-version与pyproject.toml的requires-python。 - 版本 + 虚拟环境双层隔离:pyenv/uv 管解释器,venv 管依赖。
- 用 uv 提升效率:新项目优先考虑 uv,安装与解析都更快。
- CI 用矩阵覆盖多版本:尽早发现兼容性问题。
- 保持版本栈精简:本地保留 2~3 个版本即可,过多徒增维护成本。
- 定期清理:
pyenv uninstall或uv python uninstall移除不再使用的版本。
下一节
继续学习:IDLE 与交互式解释器