82·模块十六:工程化与部署进阶

项目打包与分发:setuptools 入门

packagingsetuptoolsdistribution

第 82 课:项目打包与分发:setuptools 入门

1. 学习目标

完成本课学习后,你将能够:

  • 理解为什么要将Python项目打包,以及打包后如何分发和安装。
  • 掌握使用setuptools创建一个标准Python包的基本结构和配置文件(pyproject.toml)。
  • 学会构建并生成可分发的包(源码包和轮子包)。
  • 实践使用pip在本地安装自己创建的包,并在其他项目中导入使用。

2. 核心概念

为什么需要打包? 想象一下,你写了一个超棒的Python工具或库,想要分享给朋友或发布到网上让所有人使用。最原始的方式是把代码文件(.py)发给别人,但这样做有几个问题:

  1. 依赖管理困难:如果你的代码依赖第三方库(如requests, numpy),对方需要手动安装。
  2. 安装方式不便:用户需要知道把文件放在哪里,如何设置路径才能导入。
  3. 难以标准化:没有统一的版本号、作者信息、项目描述等元数据。

打包就是解决方案。它将你的代码、依赖声明、元数据等打包成一个标准格式的文件(如.whl.tar.gz)。然后,任何人都可以通过pip install your_package_namepip 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/,执行以下命令:

  1. 安装构建工具(如果尚未安装):

    pip install build
    
  2. 构建分发包

    python -m build
    

    执行后,你会看到项目目录下多了一个 dist/ 文件夹,里面包含两个文件:

    • mymathtools-0.1.0.tar.gz — 源码分发包
    • mymathtools-0.1.0-py3-none-any.whl — 轮子分发包(更推荐,安装更快)
  3. 在本地安装你的包

    pip install ./dist/mymathtools-0.1.0-py3-none-any.whl
    
  4. 测试使用: 在一个新的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)。你需要:

  1. 修改 geometry.pyimport numpy 并使用 numpy.pi
  2. pyproject.toml[project] 部分下添加 dependencies = ["numpy>=1.20"]
  3. 重新构建包并安装。在一个未安装 numpy 的干净环境中测试,观察 pip 是否自动为你安装了 numpy

5. 常见错误

  1. 文件路径错误:确保 pyproject.toml[tool.setuptools.packages.find]where 路径与你的实际源代码目录结构一致。使用 src 布局是推荐做法,能避免一些导入问题。
  2. 包名冲突:你选择的 name 在 PyPI 上必须是唯一的。如果只是本地测试,这没问题。但如果要公开发布,先去 pypi.org 搜索一下是否已被占用。
  3. 忘记更新版本号:每次修改后重新打包,记得递增版本号(如 0.1.0 -> 0.1.1),否则 pip 可能不会更新已安装的包。
  4. 忽略 __init__.py:这个文件是Python将一个目录视为"包"的关键。即使是空文件,也必须存在。
  5. 依赖声明缺失:如果你的代码依赖其他第三方库,必须在 dependencies 字段中明确列出,否则用户安装后运行会报 ModuleNotFoundError

6. 小结

  • 打包的意义:将你的代码、依赖、元数据标准化,使分发和安装变得简单可靠。
  • 核心工具setuptools 是Python打包的基石,配合 pyproject.toml 配置文件使用。
  • 关键步骤
    1. 规范项目结构(推荐 src 布局)。
    2. 编写 pyproject.toml 定义项目元数据。
    3. 使用 python -m build 命令构建分发包。
    4. 使用 pip install 本地安装进行测试。
  • 输出物:构建过程会生成 .tar.gz(源码包)和 .whl(轮子包),后者是更优的安装选择。

掌握项目打包是Python开发者将个人项目转化为可共享、可维护软件包的关键一步。下一课,我们将学习如何使用虚拟环境(如 PipenvPoetry)更优雅地管理项目的依赖。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「虚拟环境与依赖管理(Pipenv, Poetry)」 以巩固所学知识。