怎么将Python代码打包成可用pip安装的库

拾光
拾光
发布时间:2026-11-06 | 7人浏览过
原创

详细讲解如何将本地Python代码组织为规范工程、编写现代pyproject.toml构建配置文件,使用build工具打包生成wheel与源码包,并通过pip完成本地与跨环境安装验证。

将本地 Python 代码打包为可通过 pip 安装的库,核心分为三步:将源码封装进独立的包目录并采用规范的 src 架构,编写符合现代 PEP 517/621 标准的 pyproject.toml 声明构建系统与包元数据,最后使用官方构建前端 build 生成 .whl(Wheel)二进制归档与 .tar.gz 源码分发包。完成打包后,读者既可以在本地通过 pip install . 直接安装,也能将产物分发给团队或发布至 PyPI。
怎么将Python代码打包成可用pip安装的库
图 0:Python 库现代打包、分发与安装架构概览

一、规划项目结构:采用推荐的 src 布局

在打包之前,必须将单文件脚本或零散函数整理为具备明确导入入口的标准包。Python 社区早期流行“扁平布局”(Flat Layout),即将包目录与配置文件直接平铺在根目录下。但在实际打包与测试时,扁平布局极易导致“本地路径污染”——由于 Python 默认会将当前工作目录加入 sys.path,开发者在根目录运行测试时,程序实际加载的是工作区未经打包的源码,而非经过构建安装的库,这常常导致依赖缺失或静态资源丢失问题在分发后才暴露。

现代 Python 打包官方标准(PyPA)强烈推荐使用 src 布局(src-layout)。在这种结构下,所有供外部调用的核心模块都放置在 src/ 子目录下,根目录只保留项目级配置与说明文档。

怎么将Python代码打包成可用pip安装的库
图 2:Python 库目录结构剖析与 src 布局隔离优势

一个标准的 Python 库基础目录树如下所示:

my_project/
├── pyproject.toml       # 核心配置文件:声明构建后端、依赖与包元数据
├── README.md            # 项目说明文档,可在安装页面直接呈现
├── LICENSE              # 开源协议文件(如 MIT、Apache-2.0)
├── src/
│   └── my_package/      # 实际的 Python 库包名(外部 import 此名称)
│       ├── __init__.py  # 标识目录为 Python 包,并定义公共导出接口
│       ├── core.py      # 业务逻辑模块
│       └── utils.py     # 辅助工具模块
└── tests/
    └── test_core.py     # 自动化测试目录(不包含在打包分发产物中)

以编写一个简单计算与格式化工具库为例,在 src/my_package/core.py 中编写业务函数:

# src/my_package/core.py

def format_greeting(name: str) -> str:
    """返回规范化问候语"""
    cleaned_name = name.strip().title()
    return f"Hello, {cleaned_name}! Welcome to my_package."

def add_numbers(a: float, b: float) -> float:
    """两数相加基础算子"""
    return a + b

接着在 src/my_package/__init__.py 中显式导出给外部调用的公共 API,并定义包版本:

# src/my_package/__init__.py

from .core import format_greeting, add_numbers

__version__ = "0.1.0"
__all__ = ["format_greeting", "add_numbers", "__version__"]

二、编写现代打包配置文件:pyproject.toml

过去很多教程推荐编写 setup.py,但在现代 Python 打包标准(PEP 517、PEP 518、PEP 621)下,pyproject.toml 已经成为声明所有包信息和构建规则的唯一权威入口。它采用可读性极强的 TOML 格式,将过去散落在 setup.py、setup.cfg、requirements.txt 中的信息集中统一。

在项目根目录下创建 pyproject.toml,写入以下内容:

[build-system]
# 声明打包所需的构建工具链,pip 在构建前会自动在隔离环境中安装这些依赖
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
# 包的正式分发名称(在 pip install 中使用的名字,使用小写字母、减号或下划线)
name = "my-awesome-package"
version = "0.1.0"
description = "一个用于演示标准 pip 打包流程的示例 Python 库"
readme = "README.md"
requires-python = ">=3.8"
license = { text = "MIT" }
authors = [
    { name = "Developer", email = "developer@example.com" }
]
keywords = ["utility", "packaging", "tutorial"]
classifiers = [
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

# 声明本库运行所需的第三方外部依赖(安装本库时 pip 会自动解析并安装它们)
dependencies = [
    # 例如: "requests>=2.28.0",
]

[project.optional-dependencies]
# 可选依赖项,用户可通过 pip install "my-awesome-package[dev]" 按需安装
dev = [
    "pytest>=7.0",
    "build>=1.0",
    "twine>=4.0",
]

[project.scripts]
# 可选:如果你的库包含命令行可执行工具,在此声明命令名称与入口函数
my-tool = "my_package.core:format_greeting"
关键配置项解析:
• [build-system]:告诉 pip 使用哪个底层引擎执行打包。这里使用最为成熟的 setuptools 后端,也可根据团队习惯选用 hatchling 或 flit_core。
• name 与目录名的区别:[project].name 是库在 pip 中的注册分发名称(如 pip install my-awesome-package);而 src/ 下的目录名 my_package 是代码里 import 使用的包名(如 import my_package)。通常建议两者保持一致或使用下划线对应。

三、执行标准化构建:生成 Wheel 与源码包

准备好目录和配置文件后,即可调用标准构建前端 build 工具将源码编译封包。build 工具会在临时的完全隔离环境中解析 pyproject.toml,调用指定的后端生成分发文件。

怎么将Python代码打包成可用pip安装的库
图 1:Python 库打包与安装的标准操作步骤

1. 安装构建前端工具

在当前终端或虚拟环境中安装官方推荐的构建工具:

python -m pip install --upgrade build

2. 运行构建命令

确保当前工作目录位于包含 pyproject.toml 的项目根目录,然后执行:

python -m build
执行输出示例
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools>=61.0, wheel)
* Getting build dependencies for sdist...
* Building sdist...
* Building wheel from sdist...
Successfully built my_awesome_package-0.1.0.tar.gz and my_awesome_package-0.1.0-py3-none-any.whl

3. 检查构建产物

构建完成后,项目根目录下会自动生成 dist/ 文件夹,里面包含两种格式的打包文件:

  • .whl(Wheel 包):形如 my_awesome_package-0.1.0-py3-none-any.whl。这是现代 Python 的预构建分发标准,解压即可运行,安装速度最快。
  • .tar.gz(源码包 / sdist):包含原始源码、配置文件与文档的压缩包。当目标平台缺乏对应二进制 Wheel 时,pip 会解压该包并在本地执行构建。

四、关键认知辨析:setup.py 为什么被替代?

在网络资料中,经常看到使用 python setup.py sdist bdist_wheel 或 python setup.py install 打包安装的代码。理解这一演进历史,能帮助开发者在遇到旧项目时不产生困惑。

怎么将Python代码打包成可用pip安装的库
图 3:打包规范机制对比:setup.py vs pyproject.toml

setup.py 之所以看似合理并在过去十余年广泛使用,是因为它允许开发者用普通 Python 脚本来表达打包逻辑。然而,这种命令式脚本存在两个无法根本解决的系统性矛盾:

  • 构建依赖的循环困境:setup.py 往往依赖 setuptools 或 Cython。当一个全新的环境中没有预装这些库时,Python 甚至无法执行该文件去读取项目的依赖列表,造成了“为了安装构建依赖,必须先运行构建脚本”的死锁。
  • 不必要的代码执行与安全风险:包管理工具仅仅为了获取库的名称、版本或依赖关系,就必须在宿主机上执行整段 setup.py 代码。如果脚本包含恶意逻辑或本地环境依赖,会在探测元数据阶段直接发生执行崩溃或造成安全漏洞。

现代标准通过 pyproject.toml 将元数据定义为完全静态的声明式数据,工具无需执行任何代码即可读取版本号;同时显式声明 [build-system],使得 pip 能够在拉起构建前自动创建隔离虚拟环境并补齐构建工具链。因此,编写新库时应彻底避免手写 setup.py。

五、本地安装与两种调试模式验证

打包完成后,需要验证该库是否确实能够被 pip 正常安装,且安装后能否正确导入函数。针对不同的使用场景,pip 提供了两种安装模式。

模式一:普通静态安装(模拟最终用户场景)

你可以直接使用 pip 安装当前源码目录,或者安装 dist/ 下生成的 .whl 文件:

# 方式 A:从当前根目录直接安装
pip install .

# 方式 B:从构建生成的 wheel 包安装(推荐用于生产环境测试)
pip install dist/my_awesome_package-0.1.0-py3-none-any.whl
执行输出示例
Processing ./dist/my_awesome_package-0.1.0-py3-none-any.whl
Installing collected packages: my-awesome-package
Successfully installed my-awesome-package-0.1.0

模式二:可编辑安装(Editable Mode,专为开发调试设计)

当库的代码仍在频繁迭代时,如果每次改动一行代码都要重新打包安装,效率会极其低下。通过增加 -e 参数(即 --editable),pip 会在当前环境的 site-packages 中创建一个指向本地源码目录的符号链接或载入钩子:

pip install -e .

在此模式下,读者在 src/my_package/ 中对 Python 代码所做的任何修改,都会在下一次运行时直接生效,无需重复执行 pip install。

跨目录验证导入有效性

验证是否安装成功的关键动作是:必须切换离开项目的根目录。如果在包含 src/ 或代码文件的同级目录下运行 Python,可能会由于当前路径优先而在未安装状态下发生假性导入。

# 切换到任意其他目录(例如系统临时目录或用户主目录)
cd ~

# 启动 Python 解释器测试导入
python -c "import my_package; print(my_package.format_greeting('Alice'))"
验证输出结果
Hello, Alice! Welcome to my_package.

如果命令行中输出了格式化字符串,并且没有出现 ModuleNotFoundError,则证明该库已经完全满足 pip 安装标准。

六、常见踩坑与全生命周期自查清单

在实际打包过程中,开发者常会遇到诸如静态非代码文件丢失、导入层级错误等情况。下表列出高频问题及对应的修正方案:

常见现象 深层原因 解决方法
安装后无法导入包或报 ModuleNotFoundError 包目录下缺少 __init__.py,或者 pyproject.toml 中未正确识别 src 目录。 确保每个包和子包目录下都有 __init__.py;使用 setuptools 时可配置 [tool.setuptools.packages.find] where = ["src"]。
代码中的 .json 或 .yaml 资源文件打包后丢失 默认规则下构建系统只打包 .py 脚本,忽略了非代码静态数据资产。 在 pyproject.toml 中配置 [tool.setuptools.package-data] "my_package" = ["*.json", "assets/*"]。
本地测试正常,但在其他人机器上缺少依赖报错 依赖仅安装在个人虚拟环境中,未写入 pyproject.toml 的 dependencies 列表。 将所有运行必须的外部第三方库严格记录在 project.dependencies 数组中。
怎么将Python代码打包成可用pip安装的库
图 4:Python 库打包全生命周期自查与决策清单

按照上述决策清单逐项核对后,你的代码就已具备标准 Python 第三方库的全部要素。无论是在公司内网分发 .whl 轮子文件给他人使用、通过 Git 仓库地址直接让 pip 远程拉取(如 pip install git+https://...),还是未来使用 twine 将包上传至官方 PyPI 供全世界开发者下载,打包的核心逻辑与文件规范均完全通用。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表