一次搞定 Python 版本管理/套件安裝/虛擬環境的極速神器 - uv
| | | 1 | |
前幾天分享 Windows 與 WSL 的 pyenv 衝突的事,結果臉書跟部落格的留言區,大家排山倒海地強推 uv! (感謝大家推薦,此處就不一一言謝了)
這狀況似曾相識,上回是介紹完 JMeter 後被網友大推 K6,一試之後果真如沐春風,相見恨晚... 這回我有預感,我也馬上就要愛上 uv 了~
uv 是一款由 Astral 團隊用 Rust 語言開發的現代化 Python 套件管理及專案管理工具。
題外話:其實光是 Rust 開發這點就叫我心動了 (延伸閱讀:Rust 程式語言在 JavaScript 生態圈的角色),Rust 是強大又安全的程式語言,效能趨近 C/C++ 卻有防彈等級的記憶體安全性。但 Rust 的學習曲線偏陡峭,開發時必須與編譯器不斷鬥智鬥勇,需投入可觀時間精力方能上手,具備一定聰慧、決心與毅力才能駕御,特別吸引追求自我實現的開發者,只留給願意爬過天堂路享受特種部隊榮耀的那群人。也因為成為 Rust 開發者條件嚴苛,至今看過的 Rust 作品在效能與品質上都不曾讓人失望,因此讓我對 Rust 開發的軟體特別有好感。
uv 的定位與之前學過的 pyenv、pip、venv 有點不同,它不只解決版本管理或套件安裝的單一需求,而是要打造出全方位的解決方案,一次取代 pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv 等眾多工具。uv 整合了套件安裝、依賴管理、虛擬環境管理以及 Python 版本管理等功能,相較於傳統工具,uv 的套件安裝和依賴解析速度加快了 10 到 100 倍,而支援跨平台使用(macOS、Linux、Windows)當然也不在話下。
uv 安裝套件之所以能快如閃電的關鍵,源自 Rust 語言的高效率、並行處理與智慧化快取,以及對打包格式與解析流程的低階最佳化。以下簡要說明:
- Rust 採單一靜態執行檔,比反覆啟動 Python 解譯器快 N 倍,延遲能從數百 ms 降到 µs。
- 下載、解壓與中繼資料擷取採分層並行,比起循序處理能省下超可觀的等待。
- 透過全域快取,uv 會沿用已下載與已建置好的分發檔,在暖快取(之前下載過)情境能加快百倍,冷快取(從未下載過)時也能快 8-10 倍。
(註:此點深得我心,每個虛擬環境 pip install 重新下載安裝多複製一份,對時間跟空間都是浪費) - 針對 wheel 檔,會先讀取中央目錄、檔案表取得 dist-info/metadata,與快取現況比較,只下載短缺部分不需整檔下載,大幅減少網路傳輸量與等待時間。
(註:Wheel 檔是 Python 的標準二進位散布格式,是帶有特定命名與中繼資料結構的 ZIP 檔,安裝時可直接解壓到 site-packages 而不需現場編譯) - 建立虛擬環境與安裝檔案時會運用 Hard Link 與複製時寫入 (Copy-on-Write) 等技術,降低磁碟 I/O 與空間重複,並以原子方式更新確保一致性。
- 直接以檔案系統底層機制操作管理 venv,避免中間層成本並可提升建立/切換速度,比透過 Python 的 venv 模組更有效率。
- 採用 PubGrub 的 Rust 實作 (pubgrub-rs) 擔任版本求解器,具增量、決定性與偏好自鎖檔版本的策略,在解析過程中可預取相關套件中繼資料,消除網路延遲。
整理完 uv 特色讓我熱血沸騰,接著來實際用看看。

安裝 uv
安裝超簡單,macOS/Liunx curl -LsSf https://astral.sh/uv/install.sh | sh、Windows powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" (也可用 winget/scoop ) 都是一行搞定。
我在 WSL 測試,安裝速度很快,不到三秒完成。裝完重新登入或 source $HOME/.local/bin/env 即可使用 uv CLI。
安裝 Python
開始前先 uv python install 安裝最新版 Python,只花了 5.5s... 快到我有點不習慣。

註:以上安裝方法只會安裝 python3.13,若要將不帶版號的別名 (python、python3) 指向該版本,可加上參數 uv python install --default。
執行 Python 程式
執行程式不再是用 python <py-file-path> <args...>,要改用 uv run <py-file-path> <args...>
uv run test.py
uv run test.py "arg1" "arg2"
# bash
echo 'print("hello world!")' | uv run -
uv run - <<EOF
print("hello world!")
EOF
# cmd
echo print("hello world!") | uv run -
# PowerShell
@"
print("hello world!")
"@ | uv run -
有個小技巧是可以直接把 Python 程式寫成腳本,例如新增一個 greet 文字檔,寫入以下內容再 chmod u+x greet:
#!/usr/bin/env -S uv run --script
print("Hello, world!")
greet 便可以比照 bash 腳本直接執行,很酷。
下載套件
對於複雜應用,pyproject.toml 專案檔 可定義 Python 專案,uv 將依據它下載依賴的套件。對於單一 .py 檔,我們可以透過 --with 參數指定要下載的依賴套件。
# example.py
import time
from rich.progress import track
for i in track(range(20), description="For example:"):
time.sleep(0.05)
# 執行
uv run --with rich exmaple.py

另一個更方便的做法是用 --script 在 .py 開頭加入宣告,例如 uv add --script example.py 'requests<3' 'rich',程式最前方會有一段註刪起來的宣告語法,之後不用加 --with 參數 uv 也會依據這段宣告自動下載安裝依賴的套件:

另外,在宣告區也可指定需要的 Python 版本:
# /// script
# requires-python = ">=3.12"
# dependencies = []
# ///
# Use some syntax added in Python 3.12
type Point = tuple[float, float]
print(Point)
虛擬環境 (venv)
uv 也支援虛擬環境,但做法更先進,它會利用 Hard-Link 取代下載及複製重複的檔案到每個虛擬環境資料夾,節省寶貴空間。
uv venv # 用 .venv 資料夾建立虛擬環境
uv venv --python 3.11 # 指定 Python 版本
uv pip install ruff # 在虛擬環境安裝套件
source .venv/bin/activate # 啟用虛擬環境
# .venv\Scripts\activate # Windows
deactivate # 退出虛擬環境
快如閃電的套件安裝
接下來仔細說說 uv 讓套件安裝部署又快又好的祕密。我對這段格外好奇,便花了點時研究。

安裝 pandas 及 numpy 只花 51ms!!!
- Hard Link 重複使用
uv 會將 wheel 等套件內容集中放在全域快取目錄 (例如 ~/.cache/uv),在虛擬環境安裝 pip install 套件時不複製檔案,而是以硬連結把快取中的檔案「連結」到虛擬環境的 site-packages,因此多個環境都共用一份實體資料,不重複下載、不複製資料、不佔磁碟空間。
以 numpy 的 __init__.pyi 為例,用 ls -il 多顯示 inode 編號,可發現~/uv-lab/.venv/lib64/python3.13/site-packages/numpy/__init__.pyi與~/.cache/uv/archive-v0/vLaE7thhKt3kchyCXSCl3/numpy/__init__.pyi的 inode 編號相同都是 47044,代表這兩個檔案指向同一磁碟位址,實際上只有一份資料。而用find ~/ -xdev -inum 47044反查,一樣能證明兩個路徑都指向同一份檔案內容。

註:Hard-Link 限定同一磁碟分割,若虛擬環境與快取位於不同磁碟,uv 將退回複製檔案之傳統做法並提出警示:

延伸閱讀:理解 Symbolic Link、Hard Link 與 Directory Junction 的差異之處 by 保哥 - 原子化部署
uv 進行安裝/同步時,能像資料庫 Transaction 一樣,做到要嘛全部成功,要嘛失敗全部復原,不會出現下載一半或複製一半的情況。
其做法是先在臨時位置完成檔案展開與結構寫入,待驗證完成後再以整批移動或替換目標路徑,確保過程中如遇中斷不會留下半成品或破壞既有可用環境。
而這個設計也跟 uv 的平行處理與快取機制有關,由於檔案可能多人多處共用,故要先在隔離的臨時區並行完成解壓、連結與索引更新,再一次切換,有效降低 I/O 衝突及部分安裝的風險,確保使用者不會看到一半已更新、一半未更新的狀況。
必須說,這真是精巧的設計,使用者看不到,只會感受到又快又穩定。
結論
不用說,我被圈粉了! 今天起應該就會逐步改用 uv 取代原有的 Python 開發方式。
而現在我也已加入推坑行列:uv 真的好好用,不試看看嗎?
Introducing uv: a lightning-fast, Rust-based Python package and environment manager. Installs, manages, and runs projects efficiently, replacing many traditional tools.
Comments
# by 小黑
uv 真的頂