第 82 课:项目打包与分发:setuptools 入门
1. 学习目标
完成本课学习后,你将能够:
- 理解为什么要将Python项目打包,以及打包后如何分发和安装。
- 掌握使用
setuptools创建一个标准Python包的基本结构和配置文件(pyproject.toml)。 - 学会构建并生成可分发的包(源码包和轮子包)。
- 实践使用
pip在本地安装自己创建的包,并在其他项目中导入使用。
2. 核心概念
为什么需要打包?
想象一下,你写了一个超棒的Python工具或库,想要分享给朋友或发布到网上让所有人使用。最原始的方式是把代码文件(.py)发给别人,但这样做有几个问题:
- 依赖管理困难:如果你的代码依赖第三方库(如
requests,numpy),对方需要手动安装。 - 安装方式不便:用户需要知道把文件放在哪里,如何设置路径才能导入。
- 难以标准化:没有统一的版本号、作者信息、项目描述等元数据。
打包就是解决方案。它将你的代码、依赖声明、元数据等打包成一个标准格式的文件(如.whl或.tar.gz)。然后,任何人都可以通过pip install your_package_name或pip install your_package_file.whl一条命令完成安装,pip会自动处理依赖。
setuptools 是什么?
setuptools 是Python社区中事实标准的打包工具链。它基于一个名为 distutils 的内置模块构建,但提供了更强大、更方便的功能。我们通过一个名为 pyproject.toml 的配置文件来描述我们的项目。
3. 代码示例
我们来创建一个简单的数学工具包 mymathtools,其中包含一个函数。
第一步:创建项目结构 请按照以下结构创建文件夹和文件:
mymathtools/ # 项目根目录
├── pyproject.toml # 核心配置文件
└── src/ # 源代码目录 (推荐使用 src 布局)
└── mymathtools/
├── __init__.py # 使目录成为Python包
└── calculator.py # 我们的模块
第二步:编写代码
src/mymathtools/__init__.py:
# 这个文件可以留空,或者用于快速导入模块中的函数
# 这里我们选择从模块导入,方便用户直接使用 `from mymathtools import add`
from .calculator import add
# 定义包的版本,方便管理
__version__ = "0.1.0"
src/mymathtools/calculator.py:
"""
一个简单的数学计算模块。
"""
def add(a, b):
"""返回两个数的和。"""
return a + b
def multiply(a, b):
"""返回两个数的积。"""
return a * b
# 仅当直接运行此文件时才执行以下代码(用于简单测试)
if __name__ == "__main__":
print(f"3 + 5 = {add(3, 5)}")
print(f"4 * 6 = {multiply(4, 6)}")
第三步:配置打包元数据 (pyproject.toml)
pyproject.toml:
[build-system]
requires = ["setuptools>=61.0"] # 指定构建本包所需的工具
build-backend = "setuptools.build_meta" # 指定使用setuptools作为构建后端
[project]
name = "mymathtools" # 包的名称,将在PyPI和pip中使用
version = "0.1.0" # 版本号
authors = [
{ name="Your Name", email="[email protected]" },
]
description = "A small example package with basic math functions."
readme = "README.md" # 可选,指向README文件
requires-python = ">=3.7" # 支持的Python版本
classifiers = [ # 分类器,帮助在PyPI上分类和搜索
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
[project.urls]
Homepage = "https://github.com/yourusername/mymathtools"
Issues = "https://github.com/yourusername/mymathtools/issues"
[tool.setuptools.packages.find]
where = ["src"] # 告诉setuptools在 `src` 目录下寻找Python包
第四步:构建和安装包
打开终端(命令行),进入项目根目录 mymathtools/,执行以下命令:
-
安装构建工具(如果尚未安装):
pip install build -
构建分发包:
python -m build执行后,你会看到项目目录下多了一个
dist/文件夹,里面包含两个文件:mymathtools-0.1.0.tar.gz— 源码分发包mymathtools-0.1.0-py3-none-any.whl— 轮子分发包(更推荐,安装更快)
-
在本地安装你的包:
pip install ./dist/mymathtools-0.1.0-py3-none-any.whl -
测试使用: 在一个新的Python脚本或交互式环境中尝试:
from mymathtools import add, multiply, __version__ print(add(10, 20)) # 输出: 30 print(multiply(3, 4)) # 输出: 12 print(__version__) # 输出: 0.1.0
4. 实践练习
练习 1 (基础):修改元数据
基于上面的 mymathtools 项目,修改 pyproject.toml 文件,将项目名称改为 my_awesome_math,版本号改为 1.0.0,并添加一个项目描述。然后重新构建并安装它,验证新名称是否生效。
练习 2 (进阶):添加新模块
在 mymathtools 包中新增一个模块 geometry.py,其中包含一个计算圆面积的函数 circle_area(radius)(公式:3.14159 * radius * radius)。更新 __init__.py,使用户可以直接 from mymathtools import circle_area。构建新版本(0.1.1)的包并测试。
练习 3 (挑战):处理依赖
假设你的 circle_area 函数想要使用更精确的数学常数,需要依赖 numpy 库(numpy.pi)。你需要:
- 修改
geometry.py,import numpy并使用numpy.pi。 - 在
pyproject.toml的[project]部分下添加dependencies = ["numpy>=1.20"]。 - 重新构建包并安装。在一个未安装
numpy的干净环境中测试,观察pip是否自动为你安装了numpy。
5. 常见错误
- 文件路径错误:确保
pyproject.toml中[tool.setuptools.packages.find]的where路径与你的实际源代码目录结构一致。使用src布局是推荐做法,能避免一些导入问题。 - 包名冲突:你选择的
name在 PyPI 上必须是唯一的。如果只是本地测试,这没问题。但如果要公开发布,先去 pypi.org 搜索一下是否已被占用。 - 忘记更新版本号:每次修改后重新打包,记得递增版本号(如
0.1.0->0.1.1),否则pip可能不会更新已安装的包。 - 忽略
__init__.py:这个文件是Python将一个目录视为"包"的关键。即使是空文件,也必须存在。 - 依赖声明缺失:如果你的代码依赖其他第三方库,必须在
dependencies字段中明确列出,否则用户安装后运行会报ModuleNotFoundError。
6. 小结
- 打包的意义:将你的代码、依赖、元数据标准化,使分发和安装变得简单可靠。
- 核心工具:
setuptools是Python打包的基石,配合pyproject.toml配置文件使用。 - 关键步骤:
- 规范项目结构(推荐
src布局)。 - 编写
pyproject.toml定义项目元数据。 - 使用
python -m build命令构建分发包。 - 使用
pip install本地安装进行测试。
- 规范项目结构(推荐
- 输出物:构建过程会生成
.tar.gz(源码包)和.whl(轮子包),后者是更优的安装选择。
掌握项目打包是Python开发者将个人项目转化为可共享、可维护软件包的关键一步。下一课,我们将学习如何使用虚拟环境(如 Pipenv 和 Poetry)更优雅地管理项目的依赖。