在 Python 开发中,创建可安装的包是分享代码的重要方式。本文将深入解析两种主流打包方法——
setup.py
和pyproject.toml
,并通过一个实际项目示例,展示如何使用现代的pyproject.toml
方法构建、测试和发布 Python 包。
一、setup.py 与 pyproject.toml 的区别
1. setup.py(传统方式)
setup.py
是 Python 打包的传统方法,使用 setuptools
或 distutils
定义包的元数据和依赖关系。典型示例如下:
from setuptools import setup
setup(
name='mypackage',
version='0.1',
packages=['mypackage'],
install_requires=['requests']
)
使用方法:
python setup.py sdist bdist_wheel
pip install .
2. pyproject.toml(现代方式)
自 PEP 518 引入后,pyproject.toml
成为推荐的配置方式。它分离了构建系统配置和包元数据,支持多种构建工具(如 setuptools
、poetry
等)。示例:
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "0.1"
dependencies = ["requests"]
使用方法:
pip install .
二、为什么推荐 pyproject.toml?
- 标准化与兼容性:符合最新打包标准,与各种工具兼容性更好。
- 简化配置:分离构建系统和元数据,使配置更清晰。
- 多构建系统支持:支持多种工具,提供更大灵活性。
- 安全性:减少对自定义脚本的依赖,降低风险。
实际场景中的必要性
假设你正在开发一个复杂的机器学习库,涉及多个依赖项和复杂的构建步骤。使用 pyproject.toml
可以轻松定义这些需求,并确保在不同的开发和部署环境中保持一致性。此外,许多现代工具(如 CI/CD 系统)已经内置了对 pyproject.toml
的支持,简化了自动化流程。
构建 Python 包的最佳实践
- 新项目使用 pyproject.toml:对于新项目,推荐使用
pyproject.toml
,以符合现代打包标准并提高兼容性。 - 旧项目逐步迁移:如果维护的是已有使用
setup.py
的项目,可以继续使用,但建议在可行时迁移到pyproject.toml
。 - 结合使用:在某些情况下,可以同时使用
pyproject.toml
和setup.py
,例如用pyproject.toml
处理大部分配置,而保留一个最小化的setup.py
来处理特定功能(如构建 C 扩展)。 - 使用 setup.cfg:如果希望采用更声明式的格式但仍使用
setup.py
,可以考虑使用setup.cfg
,将元数据放在配置文件中,逻辑保留在setup.py
中。 - 利用构建工具:使用如
Poetry
或Flit
等工具,可以简化依赖管理和打包流程,自动管理pyproject.toml
和其他相关文件的创建。
三、实战示例:构建和发布一个机器学习包
下面通过一个实际的机器学习项目示例,展示如何使用 pyproject.toml
构建、测试和发布一个 Python 包。
项目概述
我们将构建一个名为 mlpredictor
的包,该包:
- 包含一个使用 scikit-learn 的简单分类器模型。
- 提供训练模型和进行预测的功能。
- 结构化以便发布到 PyPI 和 GitHub。
步骤详解
1. 创建项目结构
mlpredictor/
│
├── mlpredictor/
│ ├── __init__.py
│ ├── model.py
│
├── tests/
│ ├── test_model.py
│
├── LICENSE
├── README.md
├── pyproject.toml
└── .gitignore
2. 编写代码
mlpredictor/model.py
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import pickle
class MLPredictor:
def __init__(self):
self.model = None
def train(self):
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
iris.data, iris.target, test_size=0.2, random_state=42
)
self.model = RandomForestClassifier()
self.model.fit(X_train, y_train)
def predict(self, data):
if not self.model:
raise Exception("Model is not trained yet!")
return self.model.predict([data])
def save_model(self, path="model.pkl"):
with open(path, "wb") as f:
pickle.dump(self.model, f)
def load_model(self, path="model.pkl"):
with open(path, "rb") as f:
self.model = pickle.load(f)
mlpredictor/*init*.py
from .model import MLPredictor
__all__ = ["MLPredictor"]
3. 创建 pyproject.toml 文件
pyproject.toml
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mlpredictor"
version = "0.1.0"
description = "A simple machine learning package using scikit-learn"
authors = [
{name = "Ebrahim", email = "ebimsv0501@gmail.com"}
]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.6"
dependencies = [
"scikit-learn>=1.0",
]
[project.urls]
"Homepage" = "https://github.com/xxx_your_account/mlpredictor"
[build-system]
:指定构建系统要求,这里使用setuptools
和wheel
。[project]
:包含包的元数据,如名称、版本、描述、作者、许可证、依赖项等。
4. 编写测试
使用 pytest
添加测试。
tests/test_model.py
import pytest
from mlpredictor import MLPredictor
def test_train_and_predict():
model = MLPredictor()
model.train()
result = model.predict([5.1, 3.5, 1.4, 0.2])
assert len(result) == 1
if __name__ == "__main__":
pytest.main()
5. 添加 README、License 和 .gitignore
README.md
# MLPredictor
MLPredictor 是一个简单的机器学习包,使用 scikit-learn 训练 RandomForest 模型,并使用户能够进行预测。该包旨在演示如何打包 Python 机器学习项目以供分发。
## 特性
- 在 Iris 数据集上训练 RandomForestClassifier。
- 训练后对新数据进行预测。
- 保存和加载训练好的模型。
## 安装
您可以通过 **PyPI** 或从 **源代码** 安装该包。
### 通过 PyPI 安装
```bash
pip install mlpredictor
通过源代码安装(GitHub)
git clone https://github.com/xxx_your_account/mlpredictor.git
cd mlpredictor
pip install .
使用方法
安装后,可以使用 MLPredictor
训练模型并进行预测。
示例:训练和预测
from mlpredictor import MLPredictor
# 初始化预测器
predictor = MLPredictor()
# 在 Iris 数据集上训练模型
predictor.train()
# 对样本输入进行预测
sample_input = [5.1, 3.5, 1.4, 0.2]
prediction = predictor.predict(sample_input)
print(f"预测类别: {prediction}")
LICENSE
可以选择合适的开源许可证,如 MIT License。
.gitignore
*.pyc
__pycache__/
*.pkl
dist/
build/
6. 本地测试包
通过以下命令安装包:
pip install .
安装后,运行测试以确保一切正常:
pytest tests
注意:
-
如果使用
setup.py
,它会读取setup.py
文件以收集包元数据和安装信息,并解析和安装指定的依赖项。 -
如果使用
pyproject.toml
,它会读取该文件,可能指定构建系统要求和配置。执行上述命令后,通常会创建以下目录:- Distribution Directory:可能是
build/
、dist/
或.eggs/
目录,具体取决于安装过程以及是源码安装还是 wheel 安装。 - build/:在构建过程中创建,包含用于创建包的临时文件。
- dist/:包含从包生成的构建分发文件(如 wheel 文件)。
- egg-info/ 或 .egg-info/:包含有关已安装包的元数据,包括其依赖项和版本号。
- Distribution Directory:可能是
确保项目正常工作后,继续后续步骤。
7. 推送到 GitHub
-
初始化 Git 仓库
git init git add . git commit -m "Initial commit"
-
创建 GitHub 仓库
前往 GitHub 并创建一个名为
mlpredictor
的新仓库。 -
推送代码到 GitHub
git remote add origin https://github.com/xxx_your_account/mlpredictor.git git branch -M main git push -u origin main
注意:将
xxx_your_account
替换为您的 GitHub 用户名。
8. 发布到 PyPI
现在项目已经设置并推送到 GitHub,可以将其发布到 PyPI。
-
安装必要的工具
pip install twine build
-
构建包
python -m build
这将在
dist/
目录下创建.tar.gz
和.whl
文件。检查dist/
目录,确保包含类似以下文件:mlpredictor-0.1.0-py3-none-any.whl mlpredictor-0.1.0.tar.gz
-
上传到 PyPI
twine upload dist/*
您需要一个 PyPI 账户才能上传包。上传成功后,其他人可以通过以下命令安装您的包:
pip install mlpredictor
9. 安装并使用包
通过 pip
安装后,可以在 Python 代码中使用该包:
from mlpredictor import MLPredictor
predictor = MLPredictor()
predictor.train()
prediction = predictor.predict([5.1, 3.5, 1.4, 0.2])
print("Predicted class:", prediction.item())
# 输出示例:
# Predicted class: 0
五、总结
在 Python 打包领域,setup.py
和 pyproject.toml
各有其重要性和适用场景。尽管 setup.py
在传统项目中仍然发挥作用,但向 pyproject.toml
的转变代表了 Python 社区向更安全、标准化实践发展的趋势。对于新项目,强烈建议采用 pyproject.toml
,因为它不仅简化了打包过程,还提高了与各种工具和库的兼容性。
通过本文的实战示例,您应该能够掌握如何使用 pyproject.toml
构建、测试和发布一个功能完善的 Python 包。无论是个人项目还是团队协作,遵循这些最佳实践将大大提升项目的可维护性和可扩展性。