
Python代码打包与分发指南:从库到可执行文件,一次搞定
大家好,作为一名经常需要分享和部署Python代码的开发者,我深知打包和分发过程中的种种“坑”。今天,我想和大家深入聊聊两个最核心的工具:Setuptools(用于打包库并上传PyPI)和PyInstaller(用于生成独立的可执行文件)。这不仅仅是工具介绍,更是我踩过无数坑后总结的实战指南。无论你是想开源一个优秀的库,还是想把脚本交给没有Python环境的同事或客户,这篇文章都能帮你理清思路。
第一部分:用Setuptools打造专业的Python包
当你写了一个可复用的模块或库,并希望它可以通过pip install轻松安装时,Setuptools是你的不二之选。它负责定义项目的元数据、依赖关系,并构建分发包。
1. 项目结构标准化
一个清晰的结构是成功的一半。我推荐以下布局,这几乎是社区的标准:
my_awesome_package/
├── my_awesome_package/ # 主包目录,与项目同名
│ ├── __init__.py
│ └── core.py
├── tests/ # 测试目录
├── README.md
├── LICENSE
├── pyproject.toml # 现代构建配置核心文件
└── setup.cfg # 可选的元数据配置(传统方式)
这里的关键是,你的核心代码放在以项目名命名的子目录里,而不是散落在根目录。这能避免很多导入和打包的诡异问题。
2. 核心配置:pyproject.toml
过去我们依赖setup.py,但现在更推荐使用pyproject.toml(PEP 518和621)。它更清晰,且被所有现代工具支持。下面是一个功能齐全的示例:
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-awesome-package"
version = "0.1.0"
authors = [
{name = "Your Name", email = "you@example.com"}
]
description = "A brief description of your awesome package."
readme = "README.md"
license = {text = "MIT"}
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
keywords = ["utility", "tool"]
dependencies = [
"requests>=2.25.0",
"click>=8.0.0"
]
[project.optional-dependencies]
dev = ["pytest", "black", "flake8"]
[project.urls]
Homepage = "https://github.com/you/my_awesome_package"
Bug-Tracker = "https://github.com/you/my_awesome_package/issues"
[project.scripts]
my-cli-tool = "my_awesome_package.core:main"
这个文件定义了从构建依赖、项目信息、运行时依赖到可执行脚本入口的所有内容。project.scripts部分非常实用,它会自动在用户安装后,在系统路径中创建一个名为my-cli-tool的命令行工具。
3. 构建与上传
配置好后,打包和上传就变得非常简单:
# 安装最新构建工具
pip install --upgrade build twine
# 在项目根目录执行,生成 dist/ 下的分发包
python -m build
# 检查包内容(好习惯!)
twine check dist/*
# 上传到测试PyPI(强烈建议先测试)
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# 一切无误后,上传到正式PyPI
twine upload dist/*
踩坑提示:第一次上传前,务必在TestPyPI上测试安装流程。包名一旦被占用就无法使用,先在测试环境确认所有元数据正确无误。
第二部分:用PyInstaller生成独立可执行文件
如果你的用户是非技术人员,或者你需要分发一个带有GUI的桌面应用,让他们安装Python和一堆依赖是不现实的。这时,PyInstaller就能将你的脚本和所有依赖(包括Python解释器)打包成一个独立的.exe(Windows)、.app(macOS)或二进制文件(Linux)。
1. 基础使用与常见问题
安装和使用非常简单:
pip install pyinstaller
# 最基础的打包,生成一个庞大的文件夹
pyinstaller your_script.py
# 生成单个可执行文件(更整洁)
pyinstaller --onefile your_script.py
# 如果你不希望运行时弹出控制台窗口(GUI程序必备)
pyinstaller --onefile --windowed your_script.py
命令执行后,会在dist/目录下找到生成的可执行文件。但事情很少这么顺利,你可能会遇到:
- 文件体积巨大:这是正常的,因为它包含了迷你Python环境。使用
--onefile会稍微压缩。 - 动态库缺失:特别是使用了NumPy、PyQt、OpenCV等包含C扩展的库时,PyInstaller有时会漏掉一些动态链接库(.dll, .so)。
2. 高级配置:使用Spec文件
对于复杂项目,我强烈建议使用.spec文件。首先生成一个模板:
pyinstaller --onefile your_script.py
这会在根目录生成your_script.spec。你可以编辑这个文件,精细控制打包过程。下面是一个处理了常见隐藏导入和数据的spec文件示例:
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['your_script.py'],
pathex=[],
binaries=[],
datas=[], # 可以添加数据文件,如 ('.env', '.')
hiddenimports=['pkg_resources.py2_warn', 'sklearn.utils._weight_vector'], # 解决“隐式导入”错误
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=None,
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='your_script',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True, # 使用UPX压缩,可减小体积
runtime_tmpdir=None,
console=False, # 对应 --windowed
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
hiddenimports是解决“ModuleNotFoundError”的关键。当你的代码动态导入模块(例如通过__import__()或某些框架的插件系统)时,PyInstaller的静态分析可能找不到它们,需要在这里手动指定。
3. 实战踩坑:打包一个PyQt6应用
打包GUI应用是高频需求。以PyQt6为例,除了上述spec配置,还需要注意:
# 通常需要额外指定路径,确保找到所有资源
pyinstaller --onefile --windowed
--add-data "venv/Lib/site-packages/PyQt6/Qt6/plugins/platforms;PyQt6/Qt6/plugins/platforms"
--hidden-import PyQt6.sip
your_gui_app.py
更可靠的做法是,将上述路径和隐藏导入写入spec文件的datas和hiddenimports部分。打包后,务必在一台全新的、没有Python环境的机器上测试你的可执行文件!这是检验打包成功与否的唯一标准。
总结与工具选择
最后,简单总结一下:
- Setuptools:用于创建可分发的库(Package)。目标用户是开发者,他们通过pip安装后,在你的代码基础上进行开发。核心是
pyproject.toml。 - PyInstaller:用于创建分发给最终用户的应用程序(Application)。用户无需关心Python环境,开箱即用。核心是解决依赖收集和隐藏导入问题。
选择哪种方式,完全取决于你的代码的用途和受众。有时,一个项目甚至可以同时提供两种方式:一个通过PyPI可安装的库(包含API),和一个通过PyInstaller打包的便捷命令行工具或GUI前端。
希望这篇结合了实战经验和踩坑提示的指南,能让你在Python打包与分发的道路上走得更加顺畅。如果遇到问题,多查阅官方文档和社区讨论,大多数坑都已经有人踩过了。祝你打包顺利!

评论(0)