大家好,這裏是程式設計師晚楓,小紅書也叫這個名字。
今天給大家分享一個Python開發常見問題:如何管理計畫中數以百計的第三方庫?本文比較了多種常見方案,大家可以按需使用。
背景知識
當我開始使用 Python 並建立我的第一個包時,我很困惑。建立和管理包似乎比我預想的要困難得多。此外,存在多種工具,但我不確定該使用哪一種。我相信你們大多數人過去都遇到過同樣的問題。Python 有無數的工具來管理虛擬環境和建立包,但很難(或幾乎不可能)了解哪一個適合您的需求。存在一些關於該主題的演講和部落格文章,但它們都沒有給出完整的概述或以結構化的方式評估這些工具。這就是這篇文章的主題。
工具分類
本文劃分五個對於環境和包管理方面很重要的類別:
Python 版本管理
包管理
環境管理(主要涉及虛擬環境)
包構建
包釋出
正如下面的維因圖中所示,存在很多工具。有些是單一用途的,有些是多面手:
讓我們從開發人員的角度來瀏覽一下這些類別:假設您同時處理個人計畫和工作計畫。在工作中,您使用的是 Python 3.7,而您的個人計畫應該使用最新的 Python 版本(當前為 3.11)。換句話說:您希望能夠安裝不同的 Python 版本並在它們之間進行切換。這就是我們的第一個類別, Python 版本管理 的內容。
在您的計畫中,您正在使用其他軟體包(例如 pandas 或 sklearn 用於數據科學)。這些是您必須安裝和管理的計畫的依賴項(例如,在釋出新版本時進行升級)。這就是 包管理 的意義所在。
由於不同的計畫可能需要同一包的不同版本,因此您需要建立(和管理)虛擬環境以避免依賴沖突。用於此目的的工具收集在環境管理類別中。大多數工具使用虛擬環境,但有些工具使用另一個稱為 「本地包」(Local Packages) 的概念,我們稍後會討論。
您可能希望與其他開發人員共享您的程式碼。為此,您首先必須構建包(包構建),然後才能將其釋出到 PyPI 或其他索引(包釋出)。
下面我們將更詳細地了解每個類別,包括簡短的定義、動機和可用的工具。我將在最後的單獨部份中更詳細地介紹一些單一用途工具和一些多用途工具。我們先從第一類開始:Python 版本管理。
Python版本管理
定義
一個可以進行 Python 版本管理的工具,可以讓你輕松安裝 Python 版本並在它們之間切換。
動機
為什麽我們要使用不同的 Python 版本?有幾個原因。例如,您可能正在處理多個計畫,其中每個計畫都需要不同的 Python 版本。或者,您可能開發一個支持多個 Python 版本的計畫,並且想要測試所有版本。除此之外,檢查最新的 Python 版本所提供的功能,或者測試 Python 的預釋出版本是否存在錯誤也是不錯的選擇。
工具
我們的維因圖顯示了可用於 Python 版本管理的工具:pyenv, conda, rye 和 PyFlow。我們將首先在單獨的部份中檢視 pyenv 並考慮多用途工具。
Python 內建一個單一用途的工具,可讓您安裝和管理 Python 版本:pyenv!Pyenv 很容易使用。最重要的命令如下:
# 安裝特定版本的 Python
pyenv install 3.10.4
# 在不同的 Python 版本之間切換
# 僅針對當前 shell 切換 Python 版本
pyenv shell <version>
# 在這個目錄下,自動使用某個 Python 版本
pyenv local <version>
# 針對當前使用者,全域設定 Python 版本
pyenv global <version>
(虛擬)環境管理
定義
環境管理工具,允許您建立和管理(虛擬)環境。
動機
為什麽我們首先要使用環境?正如一開始提到的,計畫有特定的要求(即它們依賴於其他包)。通常情況下,不同的計畫需要同一包的不同版本。這可能會導致依賴沖突。此外,使用 pip install 安裝包時可能會出現問題,因為該包與系統範圍的 Python 安裝一起放置。其中一些問題可以透過使用命令 --user 中的標誌來解決。然而,這個選項可能並不是每個人都知道,尤其是初學者。
工具
許多工具允許使用者建立和管理環境。它們是:venv, virtualenv, pipenv, conda, pdm, poetry, hatch, rye 和 PyFlow。其中只有兩個是單一用途工具:venv 和 virtualenv。讓我們先來看看這兩個:
venv
venv(docs.python.org/3/library/v…) 是用於建立虛擬環境的內建 Python 包。最重要的命令如下:
# 建立一個新的環境
python3 -m venv <env_name:python-office>
# 使用這個環境
. <env_name:python-office>/bin/activate
# 關閉這個環境
deactivate
virtualenv
virtualenv (virtualenv.pypa.io/en/latest/)嘗試改進 venv. 它提供的功能更多,速度更快,功能更強大。最重要的命令與 venv 基本一樣:
# 建立一個新環境
virtualenv <env_name:python-office>
# 啟用這個環境
. <env_name:python-office>/bin/activate
# 關閉這個環境
deactivate
包管理
pyproject.toml
在我們討論打包之前,我想確保您了解打包最重要的檔:
pyproject.toml
.
Python 的打包已經取得了長足的進步,在 PEP 518 之前,setup.py 檔用於打包,setuptools 作為構建工具。PEP 518 引入了 pyproject.toml 檔的用法。因此,在建立包時始終需要一個 pyproject.toml 檔,用於定義計畫的設定、定義後設資料和許多其他內容。一個範例是 pandas 的 pyproject.toml 檔:
github.com/pandas-dev/…
[build-system]
# Minimum requirements for the build system to execute.
# See https://github.com/scipy/scipy/pull/12940 for the AIX issue.
requires = [
"meson-python==0.13.1",
"meson==1.2.1",
"python-office",
"Cython==3.0.5", # Note: sync with setup.py, environment.yml and asv.conf.json
# Any NumPy version should be fine for compiling. Users are unlikely
# to get a NumPy<1.25 so the result will be compatible with all relevant
# NumPy versions (if not it is presumably compatible with their version).
# Pin <2.0 for releases until tested against an RC. But explicitly allow
# testing the `.dev0` nightlies (which require the extra index).
"numpy>1.22.4,<=2.0.0.dev0",
"versioneer[toml]"
]
build-backend = "mesonpy"
[project]
name = 'pandas'
dynamic = [
'version'
]
description = 'Powerful data structures for data analysis, time series, and statistics'
readme = 'README.md'
authors = [
{ name = 'The Pandas Development Team', email='[email protected]' },
]
license = {file = 'LICENSE'}
requires-python = '>=3.9'
dependencies = [
"numpy>=1.22.4; python_version<'3.11'",
"numpy>=1.23.2; python_version=='3.11'",
"numpy>=1.26.0; python_version>='3.12'",
"python-dateutil>=2.8.2",
"pytz>=2020.1",
"tzdata>=2022.7"
]
classifiers = [
# 省略後續
.lock 檔
還有一個對於打包很重要的檔:釘選檔(xxx.lock)。pyproject.toml 包含抽象依賴關系,釘選檔包含具體依賴關系。它記錄了為計畫安裝的所有依賴項的確切版本(例如
pandas==2.0.3
)。這使得計畫在多個平台上具有可重復性,例如下面的 github.com/python-poet…:
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "build"
version = "1.0.3"
description = "A simple, correct python-office build frontend"
optional = false
python-versions = ">= 3.7"
files = [
{file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"},
{file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"},
]
[package.dependencies]
colorama = {version = "*", markers = "os_name == \"nt\""}
importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
packaging = ">=19.0"
pyproject_hooks = "*"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"]
test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"]
typing = ["importlib-metadata (>=5.1)", "mypy (>=1.5.0,<1.6.0)", "tomli", "typing-extensions (>=3.7.4.3)"]
virtualenv = ["virtualenv (>=20.0.35)"]
定義
包管理工具能夠下載和安裝庫及其依賴項。
動機
為什麽我們需要包?包允許我們定義模組的階層並使用"."語法,例如
from package.module import my_function
輕鬆存取模組。此外,它們還可以輕松地與其他開發人員共享程式碼。由於每個包都包含一個 pyproject.toml 定義其依賴項的檔,因此其他開發人員不必單獨安裝所需的包,而只需從其 pyproject.toml 檔中安裝該包即可。
工具
許多工具可以執行包管理:pip, pipx, pipenv, conda, pdm, poetry, rye 和 PyFlow.
其中,pip 可能是最眾所周知的,它也是這裏的專門只用來做包管理的工具。
Python 的標準包管理器是 pip(pip.pypa.io/en/stable/),它隨 Python 一起提供,並允許您從 PyPI 和其他索引安裝包。主要命令(可能是 Python 開發人員學習的第一個命令之一)是
pip install <package_name>
。
多用途工具
接下來進入非單一用途的工具
pipenv
顧名思義,pipenv 結合了 pip 和 virtualenv。它可以執行虛擬環境管理和包管理,正如我們在維因圖中看到的那樣:
pipenv 引入兩個附加檔:
Pipfile:是一個 toml 檔(類似於 pyproject.toml),用於定義計畫依賴項
Pipfile.lock:允許確定性構建。它消除了對 requirements.txt 檔的需要,並透過釘選操作自動管理。
最重要的pipenv命令是:
# 安裝某個包
pipenv install <package_name:python-office>
# 在虛擬環境執行某個指令碼
pipenv run <script_name.py>
# 啟用虛擬環境
pipenv shell
Conda
Conda 是一個通用的包管理系統。這意味著它不限於 Python 包。Conda 是一個具有很多功能的巨大工具。存在很多教程和部落格文章(例如官方的),因此我不會在這裏詳細介紹。但是,我想提一件事:雖然可以構建和釋出包,但 conda 我沒有將該工具包含在適當的類別中。這是因為打包的 conda 工作方式略有不同,並且生成的包將是 conda 包。
特征評估
接下來我將比較不同工具的以下維度的特點:
該工具是否管理依賴關系?
它是否解析/釘選依賴關系?
是否有幹凈的構建/釋出流程?
它允許使用外掛程式嗎?
它支持 PEP 660(可編輯安裝:peps.python.org/pep-0660/)嗎?PEP 660 是關於基於構建的可編輯安裝 pyproject.toml。當您使用安裝軟體包時,pip 您可以選擇使用
pip install -e package_name
來以可編輯模式安裝它。當您開發包並希望您的更改直接反映在您的環境中時,這是一個重要的功能。
它支持 PEP 621(計畫後設資料:peps.python.org/pep-0621/)嗎?PEP 621 指定如何在 pyproject.toml 檔中寫入計畫的核心後設資料。我添加這個評判維度是因為一個包(劇透:它是 poetry)當前不支持此 PEP,但使用自己的方式來聲明後設資料。
Flit
Flit(flit.pypa.io/en/stable/)嘗試建立一種簡單的方法將 Python 包和模組放在 PyPI 上。它有一個非常具體的用例:它旨在用於打包純 Python 包(即沒有構建步驟的包)。它不關心任何其他任務:
Python版本管理:❌
包管理:❌
環境管理:❌
構建包:✅
釋出包:✅
這也反映在我們的維因圖中:
該工具是否管理依賴關系?❌
它是否解析/釘選依賴關系?❌
是否有幹凈的構建/釋出流程?✅
它允許使用外掛程式嗎?❌
它支持 PEP 660 嗎?✅
它支持 PEP 621 嗎?✅
主要命令有:
# 建立新的 pyproject.toml
flit init
# 構建並釋出
flit publish
Poetry
Poetry 是眾所周知的工具。正如維因圖中所示,它可以執行除 Python 版本管理之外的所有操作:
Python版本管理:❌
包管理:✅
環境管理:✅
構建包:✅
釋出包:✅
Poetry 不支持 PEP 621。GitHub 上關於此問題的開放問題已有大約 1.5 年的時間,但還是沒有解決(github.com/python-poet…)。
該工具是否管理依賴關系?✅
它是否解析/釘選依賴關系?✅
是否有幹凈的構建/釋出流程?✅
它允許使用外掛程式嗎?✅
它支持 PEP 660 嗎?✅
它支持 PEP 621 嗎?❌
主要命令:
# 建立目錄結構和 pyproject.toml
poetry new <project_name:python-office>
# 互動式建立 pyproject.toml
poetry init
# 從 pyproject.toml 安裝依賴
poetry install
依賴管理:
# Add dependency
poetry add <package_name:python-office>
# Display all dependencies
poetry show --tree
執行程式碼
# Activate virtual env
poetry shell
# Run script within virtual env
poetry run python <script_name.py>
釘選檔:首次安裝軟體包時,Poetry 會解析 pyproject.toml 檔中列出的所有依賴項並下載最新版本的軟體包。一旦 Poetry 完成安裝,它將所有包和下載的確切版本寫入一個 poetry.lock 檔,將計畫釘選到這些特定版本。建議將釘選檔送出到您的計畫儲存庫,以便所有從事該計畫的人員都被釘選到相同版本的依賴項。要將依賴項更新到最新版本,請使用以下命令:
poetry update
構建/釋出流程:
# 打包 (建立 `.tar.gz` 和 `.whl`)
poetry build
# 釋出到 PyPI
poetry publish
pdm
pdm 是一個相對較新的包和依賴項管理器(於 2019 年開始),受到 Poetry 和 PyFlow 的啟發。您會註意到,我在本文中並沒有討論 PyFlow。這是因為 PyFlow 不再活躍,這不再適合快速發展的打包工具領域。作為一種新工具,pdm 需要 Python 3.7 或更高版本。與其他工具的另一個區別是 pdm 允許使用者選擇構建後端。
pdm 是唯一在本地包上實作 PEP 582 的工具(除 PyFlow 之外) ,這是實作環境管理的替代方法。請註意,此 PEP 最近被拒絕了。
從維因圖中可以看出,pdm 位於 Poetry 旁邊。這意味著它可以做除 Python 版本管理之外的所有事情:
Python版本管理:❌
包管理:✅
環境管理:✅
構建包:✅
釋出包:✅
pdm 的主要命令與 poetry 類似。然而,目前的命令還是比較少。例如,目前沒有 pdm shell 或 pdm new
該工具是否管理依賴關系?✅
它是否解析/釘選依賴關系?✅
是否有幹凈的構建/釋出流程?✅
它允許使用外掛程式嗎?✅
它支持 PEP 660 嗎?✅
它支持 PEP 621 嗎?✅
建立一個新計畫
# 互動式建立 pyproject.toml
pdm init
# 從 pyproject.toml 安裝包
pdm install
依賴管理
# 添加依賴
pdm add <package_name:python-office>
# 展示所有依賴
pdm list --graph
執行程式碼
# 沒有 shell 命令
# 使用當前環境執行
pdm run python <script_name.py>
釘選檔:pdm 的釘選功能與 poetry 類似。首次安裝軟體包時,pdm 會解析 pyproject.toml 檔中列出的所有依賴項並下載最新版本的軟體包。pdm 完成安裝後,會將所有包及其下載的確切版本寫入檔中 pdm.lock,從而將計畫釘選到這些特定版本。建議將釘選檔送出到您的計畫儲存庫,以便所有從事該計畫的人員都被釘選到相同版本的依賴項。要將依賴項更新到最新版本,請使用以下命令:
pdm update
構建/釋出流程:
# 打包 (建立 `.tar.gz` 和 `.whl`)
pdm build
# 釋出到 PyPI
pdm publish
Hatch
Hatch(hatch.pypa.io/latest/):
Python版本管理:❌
包管理:❌
環境管理:✅
構建包:✅
釋出包:✅
值得註意的是,Hatch 的作者承諾將很快添加釘選功能(github.com/pypa/hatch/…)。當您閱讀本文時,請務必檢查 Hatch 的最新版本,看看是否已實作此功能。
該工具是否管理依賴關系?❌
它是否解析/釘選依賴關系?❌
是否有幹凈的構建/釋出流程?✅
它允許使用外掛程式嗎?✅
它支持 PEP 660(可編輯安裝)嗎?✅
它支持 PEP 621(計畫後設資料)嗎?✅
建立一個新計畫
# 建立目錄結構以及 pyproject.toml
hatch new <project_name:python-office>
# 互動式建立計畫
hatch new -i <project_name:python-office>
# 初始化現有計畫或者建立 pyproject.toml
hatch new --init
依賴管理
# 沒有 add 命令,依賴需要手動添加到 pyproject.toml
# 展示依賴
hatch dep show table
執行程式碼
# 啟用虛擬環境
hatch shell
# 在虛擬環境執行指令碼
hatch run python <script_name.py>
構建/釋出流程
# 打包 (建立 `.tar.gz` 和 `.whl`)
hatch build
# 釋出到 PyPI
hatch publish
聲明式環境管理:Hatch 的特別之處在於它允許您在檔中配置虛擬環境 pyproject.toml。此外,它還允許您專門為環境定義指令碼。範例用例是程式碼格式化(hatch.pypa.io/1.1/config/…)。
Rye
Rye 最近由 Flask 框架的建立者 Armin Ronacher 開發(首次釋出於 2023 年 5 月)。它受到程式語言 Rust 的打包工具 rustup 和 cargo 的強烈啟發。Rye 是用 Rust 編寫的,能夠執行維因圖中的所有任務:
Python版本管理:✅
包管理:✅
環境管理:✅
構建包:✅
釋出包:✅
目前,Rye 沒有外掛程式介面。但是,由於定期釋出新版本,因此將來可能會添加此內容。
該工具是否管理依賴關系?✅
它是否解析/釘選依賴關系?✅
是否有幹凈的構建/釋出流程?✅
它允許使用外掛程式嗎?❌
它支持 PEP 660(可編輯安裝)嗎?✅
它支持 PEP 621(計畫後設資料)嗎?✅
建立一個新計畫:
# 建立目錄結構以及 pyproject.toml
rye init <project_name:python-office>
# 指定一個 Python 本本
rye pin 3.10
依賴管理:
# 添加依賴,但是還沒有安裝
rye add <package_name:python-office>
# 同步虛擬環境, 釘選檔等等
# 在這個步驟安裝依賴
rye sync
執行程式碼:
# 啟動虛擬環境
rye shell
# 使用虛擬環境執行指令碼
rye run python <script_name.py>
構建/釋出流程:
# 打包 (建立 `.tar.gz` 和 `.whl`)
rye build
# 釋出到 PyPI
rye publish
總結
Flit | Poetry | pdm | Hatch | rye | |
---|---|---|---|---|---|
該工具是否管理依賴關系 | ❌ | ✅ | ✅ | ❌ | ✅ |
它是否解析/釘選依賴關系 | ❌ | ✅ | ✅ | ❌ | ✅ |
是否有幹凈的構建/釋出流程 | ✅ | ✅ | ✅ | ✅ | ✅ |
它允許使用外掛程式嗎 | ❌ | ✅ | ✅ | ✅ | ❌ |
它支持 PEP 660(可編輯安裝)嗎 | ✅ | ✅ | ✅ | ✅ | ✅ |
它支持 PEP 621(計畫後設資料)嗎 | ✅ | ❌ | ✅ | ✅ | ✅ |
原文:https://alpopkes.com/posts/python/packaging_tools
譯文:https://juejin.cn/post/7325611798135767090
譯者:hashcon
作者在 EuroPython 2023 上的演講:https://www.youtube.com/watch?v=3-drZY3u5vo