iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0

在前幾天,我們已經建立了專案目錄與 pyproject.toml,甚至用 Hatch 建立好了虛擬環境。

那麼接下來就要面對另一個老問題:套件要怎麼安裝,版本要怎麼控管?

這不是 Python 獨有的困擾,所有語言都有同樣的痛點。因為套件會不斷更新,API 可能變動,甚至可能帶來新的 bug。如果缺乏策略,今天能跑的程式,明天就可能壞掉。


兩種常見策略

1. 直接安裝(Floating Dependency)

最直覺的方法是直接安裝:

pip install requests

這會安裝當下最新的版本。

  • 優點:快速、方便。
  • 缺點:結果不一致,今天是 2.31.0,明天可能就變成 2.32.0。團隊之間很容易出現「在我電腦能跑,在你電腦卻壞掉」的情況。

👉 適合個人實驗、一次性腳本,或版本要求不嚴格的小專案。


2. 鎖定版本(Pinned Dependency)

另一種方式是明確指定版本,在 pyproject.toml 中寫死:

[project]
dependencies = [
  "requests==2.31.0",
  "pydantic>=2.5,<2.6"
]

  • 優點
    • 團隊之間一致性高。
    • 可重現性好,五年後重建環境也能得到一樣的結果。
  • 缺點
    • 必須定期手動更新,否則會卡在舊版本(甚至連安全漏洞都保留)。
    • 更新時需要重新測試,避免相依衝突。

👉 適合團隊合作或需要長期維護的專案。


依賴版本的表達方式

Python 的 PEP 440 提供了多種版本約束語法,Hatch 也完全支援:

[project]
name = "awesome-app"
requires-python = ">=3.11,<3.13"
dependencies = [
  "requests>=2.32,<3",    # 區間鎖定(常見做法)
  "python-dateutil==2.8.*",  # 只允許 patch 升級
  "numpy~=1.26",          # 相容版號,允許 minor 內更新
]
  • 區間鎖定 (>=2.32,<3):可控範圍內升級。
  • 完全指定 (==2.8.*):僅允許 patch 變動。
  • 相容版號 (~=1.26):允許小版本更新,但不跳大版本。

👉 工程實務上,通常會依專案穩定性需求,混搭這些策略。


專案依賴 vs. 開發依賴

實務上,我們會把依賴分成兩類:

  • 專案依賴:專案運行時必須存在 → 放在 [project].dependencies
  • 開發/測試依賴:只在開發或測試階段需要 → 定義在 Hatch 環境設定或 extras

在環境中直接加入依賴:extra-dependencies

Hatch 允許你在環境下直接指定額外套件:

[tool.hatch.envs.dev]
extra-dependencies = [
  "pytest>=8.3,<9",
  "ruff==0.6.*",
  "mypy~=1.11"
]

這樣當你執行:

hatch env create dev

Hatch 就會建立一個名為 dev 的環境,並安裝這些工具。

👉 適合快速建立專用環境,但這些依賴只存在於 Hatch 的設定裡,外部工具(例如 pip、Poetry)無法重用。


抽象成 extras:optional-dependencies

若你希望這些依賴能在 不同工具中共享,應該把它們放在 pyproject.toml[project.optional-dependencies]

[project.optional-dependencies]
dev = ["pytest>=8.3,<9", "ruff==0.6.*", "mypy~=1.11"]
docs = ["mkdocs", "mkdocs-material"]

這樣除了 Hatch,其他工具也能用:

pip install '.[dev]'
pip install '.[docs]'

在 Hatch 環境中引用 extras:features

接著,你可以在 Hatch 的環境設定裡引用這些 extras:

[tool.hatch.envs.dev]
features = ["dev"]

[tool.hatch.envs.docs]
features = ["docs"]

  • hatch env create dev → 會自動安裝 pytestruffmypy
  • hatch env create docs → 會自動安裝 mkdocsmkdocs-material

👉 差別在於重用性

  • extra-dependencies → 只能在 Hatch 裡用。
  • features → 直接連結到 extras([project.optional-dependencies]),能跨工具重用。

對比整理

寫法 範例 適合情境
extra-dependencies [tool.hatch.envs.dev].extra-dependencies = ["pytest"] 只想在 Hatch 中加特定套件,不需要跨工具支援
optional-dependencies + features [project.optional-dependencies].dev = [...] [tool.hatch.envs.dev].features = ["dev"] 希望依賴群組可被 pip/Poetry/Hatch 等工具重用,是工程化最佳實務

完整範例

[project]
name = "awesome-app"
requires-python = ">=3.11,<3.13"
dependencies = [
  "requests>=2.32,<3",
  "numpy~=1.26",
]

[project.optional-dependencies]
dev = ["pytest>=8.3,<9", "ruff==0.6.*", "mypy~=1.11"]
docs = ["mkdocs", "mkdocs-material"]

[tool.hatch.envs.dev]
features = ["dev"]

[tool.hatch.envs.docs]
features = ["docs"]

[tool.hatch.envs.experiment]
extra-dependencies = ["rich", "httpx"]

  • dev → 引用 extras → 跨工具都能用
  • docs → 同上
  • experiment → 只在 Hatch 裡多裝 richhttpx,不對外公開

在上述的設定完成後,你可以使用這個指令

hatch env show

他就可以在你的專案,顯示出相關的環境設定

https://ithelp.ithome.com.tw/upload/images/20250920/20178117Znd020lApl.png

結論

身為 Java 開發者,我習慣透過切換不同的 JDK 版本來因應需求,但在 Python 世界,虛擬環境能做到更高層次的隔離與依賴管理。雖然各有優缺點,但這種便利性確實讓我對 Python 工程化有新的體驗。

而在本章的重點就是,依賴管理的本質,就是在 「快」「穩」 之間取捨:

  • 快速實驗extra-dependencies,直接在環境加需要的套件。
  • 長期維護optional-dependencies + features,抽象成 extras,確保跨工具一致性。
  • 工程化 → Hatch 提供靈活選項,既能快速擴充,又能標準化。

明天我們將進入 Day 7 - 可重現環境策略:constraints / lock 與快取,來進一步探討如何讓環境真正可重現。這就有點像 TypeScript/Node.js 專案中的 package-lock.jsonyarn.lock,究竟 Python 世界的解法是什麼呢?我們接著看下去。


上一篇
Day 5 - Hatch 基本操作:建立與管理虛擬環境
下一篇
Day 7 -可重現環境策略:constraints / lock 與快取
系列文
30 天 Python 專案工坊:環境、結構、測試到部署全打通28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言