6.2 建構系統比較
6.2 建構系統比較
本章比較主流的 Python 套件建構系統。
本章目標
學完本章後,你將能夠:
- 理解不同建構系統的設計理念
- 根據專案需求選擇適合的工具
- 在不同工具之間遷移
【總覽】建構系統生態
建構後端 vs 建構前端
1建構前端(Frontend):使用者互動的工具
2├── pip
3├── build
4└── 各工具的 CLI(poetry, hatch, etc.)
5
6建構後端(Backend):實際執行建構的程式
7├── setuptools.build_meta
8├── flit_core.buildapi
9├── hatchling.build
10├── poetry.core.masonry.api
11└── maturin, scikit-build-core, mesonpy主流工具比較
| 工具 | 定位 | 特點 | 適用場景 |
|---|---|---|---|
| setuptools | 建構後端 | 歷史最久,功能最全 | 一般專案、C 擴展 |
| Flit | 建構後端 | 極簡設計 | 純 Python 小型專案 |
| Hatch | 全套工具 | 現代設計,環境管理 | 新專案 |
| Poetry | 全套工具 | 依賴鎖定,虛擬環境 | 應用程式開發 |
| PDM | 全套工具 | PEP 582,快速 | 實驗性專案 |
【工具一】setuptools
特點與定位
1setuptools:
2├── Python 打包的「標準」
3├── 歷史最悠久(2004 年開始)
4├── 支援所有功能(C 擴展、資料檔案等)
5├── 學習曲線較陡
6└── PEP 621 支援(61.0.0+ 版本)基本設定
1# pyproject.toml
2[build-system]
3requires = ["setuptools>=61.0"]
4build-backend = "setuptools.build_meta"
5
6[project]
7name = "my-package"
8version = "1.0.0"
9dependencies = ["requests"]
10
11[tool.setuptools.packages.find]
12where = ["src"]進階設定
1[tool.setuptools]
2# 明確指定套件
3packages = ["my_package", "my_package.submodule"]
4
5# 包含資料檔案
6package-data = {"my_package" = ["*.json", "data/*"]}
7
8# 排除檔案
9exclude-package-data = {"my_package" = ["test_*"]}
10
11# Zip 安全(是否可以作為 zip 檔案匯入)
12zip-safe = false
13
14[tool.setuptools.dynamic]
15version = {attr = "my_package.__version__"}
16readme = {file = ["README.md"]}C 擴展支援
1# pyproject.toml
2[build-system]
3requires = ["setuptools>=61.0", "cython>=3.0"]
4build-backend = "setuptools.build_meta" 1# setup.py(仍需要用於複雜的 C 擴展)
2from setuptools import setup, Extension
3from Cython.Build import cythonize
4
5extensions = [
6 Extension(
7 "my_package.fast_module",
8 ["src/my_package/fast_module.pyx"],
9 )
10]
11
12setup(
13 ext_modules=cythonize(extensions),
14)常用命令
1# 建構
2python -m build
3
4# 可編輯安裝
5pip install -e .
6
7# 開發模式(含額外依賴)
8pip install -e ".[dev]"【工具二】Flit
特點與定位
1Flit:
2├── 極簡設計
3├── 只支援純 Python 套件
4├── 不支援 C 擴展
5├── 快速建構
6└── 適合簡單函式庫基本設定
1# pyproject.toml
2[build-system]
3requires = ["flit_core>=3.4"]
4build-backend = "flit_core.buildapi"
5
6[project]
7name = "my-package"
8version = "1.0.0"
9description = "A simple package"
10authors = [{name = "Your Name", email = "you@example.com"}]
11dependencies = ["requests"]
12
13# Flit 自動從 __init__.py 讀取 docstring 和 version
14# 所以常常不需要設定 description 和 version1# src/my_package/__init__.py
2"""A simple package for doing things."""
3__version__ = "1.0.0"常用命令
1# 安裝 flit
2pip install flit
3
4# 建構
5flit build
6
7# 發布
8flit publish
9
10# 可編輯安裝
11flit install --symlink # Unix
12flit install --pth-file # Windows限制
1Flit 不支援:
2├── C/C++/Rust 擴展
3├── 複雜的建構腳本
4├── 資料檔案的細粒度控制
5├── 動態版本(需要在 __init__.py 中定義)
6└── 依賴鎖定【工具三】Hatch
特點與定位
1Hatch:
2├── 現代化設計
3├── 環境管理(類似 tox)
4├── 版本管理
5├── 腳本系統
6├── PEP 標準優先
7└── 由 PyPA 成員維護基本設定
1# pyproject.toml
2[build-system]
3requires = ["hatchling"]
4build-backend = "hatchling.build"
5
6[project]
7name = "my-package"
8version = "1.0.0"
9dependencies = ["requests"]
10
11[tool.hatch.build.targets.wheel]
12packages = ["src/my_package"]
13
14[tool.hatch.build.targets.sdist]
15include = ["/src", "/tests"]環境管理
1# 定義環境
2[tool.hatch.envs.default]
3dependencies = ["pytest", "pytest-cov"]
4
5[tool.hatch.envs.default.scripts]
6test = "pytest {args:tests}"
7cov = "pytest --cov=my_package {args:tests}"
8
9[tool.hatch.envs.lint]
10dependencies = ["ruff", "mypy"]
11
12[tool.hatch.envs.lint.scripts]
13check = ["ruff check .", "mypy src"]
14fix = "ruff check --fix ."
15
16[tool.hatch.envs.docs]
17dependencies = ["sphinx", "sphinx-rtd-theme"]
18
19[tool.hatch.envs.docs.scripts]
20build = "sphinx-build -b html docs docs/_build"版本管理
1[tool.hatch.version]
2path = "src/my_package/__init__.py"
3
4# 或使用 hatch-vcs 從 git 標籤讀取
5# [tool.hatch.version]
6# source = "vcs"
7#
8# [tool.hatch.build.hooks.vcs]
9# version-file = "src/my_package/_version.py"常用命令
1# 安裝 hatch
2pip install hatch
3
4# 建立新專案
5hatch new my-package
6
7# 執行腳本
8hatch run test
9hatch run lint:check
10
11# 進入環境 shell
12hatch shell
13
14# 版本管理
15hatch version # 顯示當前版本
16hatch version minor # 升級 minor 版本
17hatch version 2.0.0 # 設定特定版本
18
19# 建構
20hatch build
21
22# 發布
23hatch publish【工具四】Poetry
特點與定位
1Poetry:
2├── 依賴解析與鎖定
3├── 虛擬環境管理
4├── 發布流程整合
5├── 自己的設定格式([tool.poetry])
6├── Poetry 2.0 支援 [project] 表
7└── 適合應用程式開發基本設定(Poetry 2.0+)
1# pyproject.toml(Poetry 2.0 風格)
2[build-system]
3requires = ["poetry-core>=2.0"]
4build-backend = "poetry.core.masonry.api"
5
6[project]
7name = "my-package"
8version = "1.0.0"
9description = "My awesome package"
10authors = [{name = "Your Name", email = "you@example.com"}]
11dependencies = ["requests>=2.28"]
12requires-python = ">=3.8"
13
14[project.optional-dependencies]
15dev = ["pytest>=7.0", "ruff"]
16
17# Poetry 特定設定仍在 [tool.poetry]
18[tool.poetry]
19packages = [{include = "my_package", from = "src"}]
20
21[tool.poetry.group.dev.dependencies]
22pytest = "^7.0"
23ruff = "^0.1"傳統設定(Poetry 1.x)
1# pyproject.toml(舊風格,仍支援)
2[tool.poetry]
3name = "my-package"
4version = "1.0.0"
5description = "My awesome package"
6authors = ["Your Name <you@example.com>"]
7
8[tool.poetry.dependencies]
9python = "^3.8"
10requests = "^2.28"
11
12[tool.poetry.group.dev.dependencies]
13pytest = "^7.0"
14ruff = "^0.1"
15
16[build-system]
17requires = ["poetry-core>=1.0.0"]
18build-backend = "poetry.core.masonry.api"依賴鎖定
1# poetry.lock 檔案
2# - 記錄所有依賴的精確版本
3# - 確保團隊成員使用相同版本
4# - 應該提交到版本控制
5
6# 安裝(使用 lock 檔案)
7poetry install
8
9# 更新依賴
10poetry update
11
12# 新增依賴
13poetry add requests
14poetry add pytest --group dev
15
16# 移除依賴
17poetry remove requests常用命令
1# 安裝 poetry
2pip install poetry
3# 或官方推薦的安裝方式
4curl -sSL https://install.python-poetry.org | python3 -
5
6# 建立新專案
7poetry new my-package
8poetry init # 在現有目錄初始化
9
10# 虛擬環境
11poetry env use python3.11
12poetry shell # 進入虛擬環境
13
14# 執行命令
15poetry run python script.py
16poetry run pytest
17
18# 建構與發布
19poetry build
20poetry publish
21
22# 設定 PyPI token
23poetry config pypi-token.pypi <your-token>【工具五】PDM
特點與定位
1PDM:
2├── 支援 PEP 582(__pypackages__)
3├── 快速的依賴解析
4├── 支援 PEP 621
5├── 插件系統
6└── 實驗性功能多基本設定
1# pyproject.toml
2[build-system]
3requires = ["pdm-backend"]
4build-backend = "pdm.backend"
5
6[project]
7name = "my-package"
8version = "1.0.0"
9dependencies = ["requests"]
10
11[tool.pdm]
12distribution = true
13
14[tool.pdm.dev-dependencies]
15dev = ["pytest", "ruff"]常用命令
1# 安裝 pdm
2pip install pdm
3
4# 初始化專案
5pdm init
6
7# 依賴管理
8pdm add requests
9pdm add -d pytest # 開發依賴
10pdm remove requests
11pdm update
12
13# 執行
14pdm run python script.py
15pdm run pytest
16
17# 建構
18pdm build
19pdm publish【選擇指南】決策流程
決策樹
1需要選擇建構系統?
2│
3├── 需要 C/Rust 擴展?
4│ ├── 是 → setuptools + Cython/pybind11
5│ │ 或 maturin(Rust)
6│ └── 否 ↓
7│
8├── 需要依賴鎖定?
9│ ├── 是 → Poetry 或 PDM
10│ └── 否 ↓
11│
12├── 需要環境管理?
13│ ├── 是 → Hatch 或 Poetry
14│ └── 否 ↓
15│
16├── 極簡專案?
17│ ├── 是 → Flit
18│ └── 否 → setuptools 或 Hatch場景建議
1場景 推薦工具
2─────────────────────────────────────────────────────
3純 Python 函式庫 Flit 或 Hatch
4Web 應用程式 Poetry
5資料科學專案 Poetry 或 PDM
6有 C 擴展的函式庫 setuptools
7Rust 擴展 Maturin
8開源專案(多貢獻者) Hatch 或 setuptools
9內部工具 Poetry遷移考量
1從 setup.py 遷移:
2├── 純 Python → 任何工具都可以
3├── 有 C 擴展 → setuptools(保留部分 setup.py)
4└── 複雜建構 → 保持 setuptools
5
6從 Poetry 1.x 遷移到 2.0:
7├── 更新 poetry-core 版本
8├── 可選:將 [tool.poetry.dependencies] 移到 [project]
9└── 測試建構和安裝
10
11工具之間遷移:
12├── 所有現代工具都支援 PEP 621
13├── 主要差異在 [tool.xxx] 設定
14└── 依賴鎖定檔案不相容【比較】功能對照表
核心功能
| 功能 | setuptools | Flit | Hatch | Poetry |
|---|---|---|---|---|
| PEP 621 | 支援 | 支援 | 支援 | 支援 (2.0) |
| C 擴展 | 支援 | 不支援 | 不支援 | 不支援 |
| 環境管理 | 不支援 | 不支援 | 支援 | 支援 |
| 依賴鎖定 | 不支援 | 不支援 | 不支援 | 支援 |
| 腳本系統 | 不支援 | 不支援 | 支援 | 支援 |
| 版本管理 | 有限 | 不支援 | 支援 | 支援 |
| 插件系統 | 支援 | 不支援 | 支援 | 支援 |
效能比較
1建構速度(純 Python 專案):
2Flit > Hatch > Poetry > setuptools
3
4依賴解析速度:
5PDM > Poetry > pip
6
7安裝速度:
8pip + lock file > Poetry > PDM思考題
- 為什麼 Python 社群有這麼多打包工具?這是好事還是壞事?
- 依賴鎖定對函式庫和應用程式的重要性有什麼不同?
- 如果要開始一個新的開源專案,你會選擇哪個工具?為什麼?
實作練習
- 用 setuptools、Flit、Hatch 三種工具建立相同的簡單套件,比較設定檔的差異
- 使用 Poetry 建立一個有依賴鎖定的專案,模擬團隊協作場景
- 將一個現有的 setup.py 專案遷移到 pyproject.toml
延伸閱讀
上一章:pyproject.toml 完整指南 下一章:發布到 PyPI