快轉到主要內容
  1. 教學文章/

uv 管理 Python 版本:Install、Find、Pin、Upgrade 與直譯器選擇

·9 分鐘· loading · loading · ·
Python Uv Python Versions Interpreter Virtualenv Developer-Tools
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
Python 學習 - 本文屬於一個選集。
§ 106: 本文

featured

一. 前言:你執行的 Python,真的是你以為的那一個嗎?
#

電腦裡只有一個 python 的日子,通常很短。 專案一多,環境很快就會變成這樣:

  • macOS 或 Linux 自己附了一套 Python
  • Homebrew、apt 或其他套件管理器又裝了一套
  • 舊專案仍然需要 Python 3.11
  • 新專案已經準備升到 Python 3.14
  • .venv 裡還有另一個直譯器
  • CI 使用的版本和本機不完全相同 這時候最危險的不是「沒有 Python」,而是指令看似成功,實際上選錯直譯器。 你用 python --version 看到 3.14,uv run python 卻跑 3.12;你更新了系統 Python,舊 .venv 仍然連向另一個安裝位置。問題不在套件,而在 interpreter selection。 拍拍君今天只處理 uv 的 Python 版本管理:
  1. 分清楚 managed Python 與 system Python
  2. 安裝、列出並定位直譯器
  3. 用 request、pin 與專案契約控制選擇
  4. 安全升級 patch release
  5. 建立團隊與 CI 都能重現的診斷流程 如果你想學 uv adduv syncuv run 與 lockfile,先看 uv 完全教學uv workspace 實戰。如果你正在比較 venv、conda 與 uv 的定位,請看 Python 虛擬環境大比拼

二. 安裝 uv,先確認目前狀態
#

macOS 與 Linux 可以使用官方 installer: curl -LsSf https://astral.sh/uv/install.sh | sh macOS 也可以用 Homebrew: brew install uv Windows PowerShell: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 先確認版本: uv --version 接著做三個不會改動環境的檢查:

uv python find
uv python list --only-installed
uv python dir

它們分別回答:

  • uv 此刻會選到哪一個 Python?
  • uv 能看到哪些已安裝版本?
  • uv-managed Python 被放在哪裡? 這三個問題比直接猜 which python 更完整,因為 uv 不只搜尋 shell 的 PATH,也會考慮虛擬環境與自己管理的安裝。

三. Managed Python 與 System Python
#

uv 把 Python 安裝分成兩類。

Managed Python
#

由 uv 下載與安裝的版本,稱為 managed Pythonuv python install 3.13 uv 會使用可攜式的預建 Python distribution,把它放進 uv 的安裝目錄,並管理對應的 executable。 用下面指令查看實際位置:

uv python dir
uv python dir --bin

System Python
#

不是 uv 安裝的版本,對 uv 而言都屬於 system Python。 這裡的「system」比作業系統內建更廣:

  • /usr/bin/python3
  • Homebrew 安裝的 Python
  • 官方 installer 安裝的 Python
  • pyenv 管理的 Python
  • Windows registry 裡可發現的 Python 即使 Python 是 pyenv 安裝的,uv 仍把它歸類為 system Python,因為它不是 uv 自己管理的安裝。 這個分類很重要,因為 uv python upgrade 只能升級 uv-managed Python,不能接管 Homebrew、pyenv 或作業系統的 Python。

四. 安裝版本:精確版、最新 Patch 與多版本
#

安裝某個 minor line 的最新 patch: uv python install 3.12 安裝精確版本: uv python install 3.12.12 一次安裝多個版本: uv python install 3.11 3.12 3.13 也可以用範圍: uv python install '>=3.11,<3.13' 或指定其他 implementation: uv python install pypy@3.11 如果沒有帶版本,uv 會讀取目前的版本請求;沒有請求時則安裝最新可用版本: uv python install

為什麼新版本可能找不到?
#

uv 可下載的 Python 清單會隨 uv release 更新。剛發布的新 patch 或新 minor line 如果沒有出現在清單裡,先更新 uv,再重新查詢:

uv self update
uv python list 3.14

若 uv 是透過 Homebrew 或其他系統套件管理器安裝,請用原套件管理器更新,而不是混用更新方式。

五. Executable 與 PATH:為什麼裝完沒有 python
#

預設安裝 Python 3.13 時,uv 會提供帶版本的 executable,例如: python3.13 它不一定會覆蓋通用的 pythonpython3。這是保守而合理的行為,避免悄悄搶走系統既有指令。 先查看 executable 目錄: uv python dir --bin 如果該目錄尚未進入 PATHuv python update-shell 重新開 shell,再檢查:

command -v python3.13
python3.13 --version

若你真的要讓 uv 提供 pythonpython3,目前可以使用 experimental --defaultuv python install 3.13 --default 團隊文件最好仍使用 uv run 或帶版本的 executable,不要假設每個人的 python 都指向同一處。

六. List:讀懂「已安裝」與「可下載」
#

最基本的查詢: uv python list 輸出同時包含已安裝版本與可下載版本。要縮小範圍:

uv python list 3.13
uv python list pypy
uv python list --only-installed

只看 uv 管理的版本: uv python list --managed-python 排除 uv-managed Python,只看 system Python: uv python list --no-managed-python 診斷時別只截取版本號,也保留路徑:

cpython-3.14.x-macos-aarch64-none  /opt/homebrew/bin/python3.14
cpython-3.12.x-macos-aarch64-none  ~/.local/share/uv/python/.../python3.12

同樣是 CPython,來源、架構與維護者都可能不同。路徑才是定位問題的證據。

七. Find:問 uv 最後會選誰
#

list 是盤點,find 才是選擇結果: uv python find 找特定 minor line: uv python find 3.12 找符合範圍的版本: uv python find '>=3.11,<3.14' 找特定 implementation: uv python find pypy@3.11 也能直接請求 executable path: uv venv --python /opt/homebrew/bin/python3.14

.venv 為什麼總是插隊?
#

如果目前目錄或父目錄存在 .venv,或 VIRTUAL_ENV 已設定,uv 通常會先考慮虛擬環境中的直譯器。 所以除錯時要一起檢查:

printf '%s\n' "${VIRTUAL_ENV:-<not set>}"
uv python find
uv run python -c 'import sys; print(sys.executable)'

想忽略虛擬環境並查 system interpreter: uv python find --system 這比 which python 更能回答「uv 執行專案時會用誰」。

八. Python Request:不只可以寫 3.12
#

uv 接受多種版本請求。 常用格式如下:

請求 意義
3.12 任何相容的 CPython 3.12 patch
3.12.12 精確 patch
>=3.11,<3.14 版本範圍
cpython@3.13 指定 implementation 與版本
pypy@3.11 指定 PyPy
3.13t free-threaded variant 的簡寫
/path/to/python 指定本機 executable
大多數 uv 指令都能用 --python 提出請求:
uv venv --python 3.12
uv run --python 3.13 python -V
uv sync --python 3.12

請求不是「把所有地方永久改成 3.12」,而是告訴這次操作需要什麼 interpreter。要保存專案預設值,使用 pin。

九. Pin:把預設請求寫進專案
#

在專案根目錄執行: uv python pin 3.12 uv 會建立 .python-version3.12 查看目前 pin: uv python pin 通常應該把 .python-version commit 進 Git,讓開發者進入專案時得到相同的預設請求。

精確 Patch 還是 Minor Line?
#

一般應用程式多半 pin minor line: 3.12 這樣可以取得同一 minor line 的安全 patch 更新。只有在重現特定 runtime bug、二進位相容性測試或嚴格驗證環境時,才考慮: 3.12.12 另外,雖然 uv 支援複雜 request,若 .python-version 也要讓 pyenv、mise 等工具讀取,單純版本號相容性最好。

Global Pin 要小心
#

uv 也支援: uv python pin --global 3.13 它適合設定個人預設值,但不能取代每個專案的明確契約。團隊 repo 應該依賴 repo 內的設定,不要依賴某位開發者的 global pin。

十. .python-versionrequires-python 不同
#

這兩個欄位常被混在一起,其實用途不同。 .python-version 是本機工具的 預設 interpreter request3.12 pyproject.tomlrequires-python 是專案對使用者與 resolver 宣告的 支援範圍

[project]
name = "pypy-tool"
version = "0.1.0"
requires-python = ">=3.11,<3.14"

一個合理組合可以是:

  • .python-version3.12
  • requires-python>=3.11,<3.14
  • CI matrix:3.11、3.12、3.13 開發者平常用 3.12,套件仍承諾支援三個 minor line,CI 負責驗證承諾。 如果 pin 與 requires-python 互相衝突,uv 不應該被迫猜測。修正設定,別用臨時 flag 掩蓋 repo 的契約錯誤。

十一. 自動下載:方便,但要知道怎麼關
#

uv 找不到符合條件的 Python 時,預設可以自動下載 managed Python。 例如: uv venv --python 3.12 即使機器原本沒有 3.12,也可能直接完成。對新手與乾淨 CI runner 很方便,對離線環境或受控企業環境則可能太主動。 單次禁止下載: uv --no-python-downloads venv --python 3.12 設定檔也能固定政策:

[tool.uv]
python-downloads = "never"

關閉自動下載後,缺少版本就應該明確失敗。這在 air-gapped CI、預先製作的 container image 與需要審核 runtime 來源的環境很有價值。

十二. Managed 還是 System:明確設定偏好
#

預設情況下,uv 可以同時發現 managed 與 system Python。需要更嚴格時可使用旗標。 只允許 uv-managed Python: uv run --managed-python python -V 只允許 system Python: uv run --no-managed-python python -V 常見政策:

  • 個人開發機:偏好 managed,降低系統差異
  • 公司基礎 image:只用預裝的 system Python
  • CI:明確 install,再用 uv python find 留下路徑證據
  • 發行版維護:避免讓 uv 自動下載另一套 runtime 關鍵不是哪一類永遠比較好,而是不要讓選擇只存在於某台機器的 PATH 偶然順序裡。

十三. Upgrade:只升 Patch,不跨 Minor
#

uv python upgrade 用來升級 uv-managed CPython 的 patch release,例如從 3.12.x 升到更新的 3.12.y。 目前這項功能仍是 preview,因此指令要明確開啟: uv --preview python upgrade 3.12 升級全部 managed CPython: uv --preview python upgrade 它不會自動把 3.12 變成 3.13,因為 minor upgrade 可能改變語法、ABI 與 dependency resolution。跨 minor line 應該是一次明確遷移:

uv python install 3.13
uv python pin 3.13
uv lock
uv sync
uv run pytest

升級 patch 後,舊安裝不會立刻被刪除,因為既有環境可能仍然使用它。新式 uv-managed 虛擬環境可以跟隨 minor-version link 升級;但精確 pin patch、舊環境或被其他工具 canonicalize 過的路徑可能不會透明更新。 因此升級後一定要驗證:

uv python find 3.12
uv run python -c 'import platform, sys; print(platform.python_version()); print(sys.executable)'
uv run pytest

十四. 團隊工作流:把版本變更當成 PR
#

一個乾淨的 repository 可以同時保存:

project/
├── .python-version
├── pyproject.toml
├── uv.lock
├── src/
└── tests/

升級 minor line 時,不要只改 .python-version。建議在同一個 PR 完成:

  1. 修改 .python-version
  2. 檢查 requires-python
  3. 更新 CI matrix
  4. 重新產生或驗證 uv.lock
  5. 執行測試、lint 與 type check
  6. 記錄不相容 dependency 或 runtime 行為 最小診斷 script:
from __future__ import annotations
import json
import platform
import sys
report = {
    "version": platform.python_version(),
    "implementation": platform.python_implementation(),
    "executable": sys.executable,
    "prefix": sys.prefix,
    "base_prefix": sys.base_prefix,
    "platform": platform.platform(),
}
print(json.dumps(report, indent=2, ensure_ascii=False))

用 uv 執行: uv run python scripts/runtime_report.py Bug report 附上這份輸出,通常比一句「我用 Python 3.12」有效得多。

十五. 常見問題與排查順序
#

問題一:uv python find 找到 .venv,不是剛安裝的版本
#

先確認目前是否位於舊專案或已啟用環境:

pwd
printf '%s\n' "${VIRTUAL_ENV:-<not set>}"
uv python find
uv python find --system

需要重建環境時,先確認沒有未保存的本機依賴資訊,再移除並執行 uv sync。不要只改 symlink 讓環境看似更新。

問題二:裝了 Python,shell 仍找不到
#

uv python dir --bin
uv python update-shell

重新開 shell,再用 command -v python3.x 確認。shell 可能快取舊位置,必要時執行 hash -r

問題三:同版本卻裝不了 wheel
#

檢查 architecture 與 implementation: uv run python -c 'import platform; print(platform.machine(), platform.python_implementation())' Apple Silicon 上的 arm64 與經 Rosetta 執行的 x86_64 不能混用 native wheel。版本號相同,不代表平台 tag 相同。

問題四:upgrade 沒有跨到下一個 minor line
#

這是設計,不是 bug。upgrade 只升 patch;跨 minor 要 install、pin、resolve、test。

問題五:想清理舊版本
#

先盤點: uv python list --only-installed 確認沒有專案與虛擬環境依賴後,再移除指定的 managed Python: uv python uninstall 3.11 uv 不會替你移除 Homebrew、pyenv 或 OS 管理的 system Python;請回到原安裝工具處理。

結語
#

uv 管理 Python 版本的價值,不只是少裝一個 pyenv。 真正有用的是,它把「Python 在哪裡、從哪裡來、為什麼被選中、專案需要哪個版本」變成可以查詢與 commit 的資訊。 記住這組分工:

  • uv python install:讓版本存在
  • uv python list:盤點可用與已裝版本
  • uv python find:確認實際選擇
  • uv python pin:保存專案預設 request
  • requires-python:宣告專案支援範圍
  • uv --preview python upgrade:升級 managed CPython patch 下次遇到「明明都是 Python 3.12,為什麼只有這台會壞」,先不要重裝整個世界。請 uv 把 interpreter 的身分與路徑說清楚,通常真相就出現了。🔎

延伸閱讀
#

Python 學習 - 本文屬於一個選集。
§ 106: 本文

相關文章

uv + GitHub Actions 實戰:Lockfile、Cache 與可重現 CI
·5 分鐘· loading · loading
Python Uv GitHub Actions Ci Lockfile Cache Developer-Tools
Python uv build/publish 實戰:從 wheel 到 private package workflow
·11 分鐘· loading · loading
Python Uv Packaging Wheel PyPI Private-Package Developer-Tools
Python uv scripts 實戰:PEP 723、inline dependencies 與單檔工具
·6 分鐘· loading · loading
Python Uv PEP 723 Script Developer-Tools Automation
Python sysconfig 實戰:安裝路徑、編譯資訊與環境診斷
·6 分鐘· loading · loading
Python Sysconfig Standard-Library Packaging Virtualenv Developer-Tools
Textual Form Wizard 實戰:多步驟表單、Validation 與狀態切換
·5 分鐘· loading · loading
Python Textual TUI Forms Validation Developer-Tools
Python OpenTelemetry 實戰:Trace、Span 與 FastAPI 觀測流程
·6 分鐘· loading · loading
Python OpenTelemetry Observability FastAPI Tracing Developer-Tools