介绍
Python 包是组织和分发代码的强大方式。虽然 Python 脚本(.py 文件)构成了包的核心,但你通常需要包含其他文件,例如配置文件、数据文件、模板或文档。本教程将指导你创建一个包含这些附加资源的 Python 包,使你的包更通用、更有用。
在本实验结束时,你将创建一个包含附加文件的完整 Python 包,并学习如何从你的代码中访问这些文件。
创建一个基本的 Python 包结构
让我们从创建一个基本的 Python 包结构开始。一个包本质上是一个包含 Python 模块和一个特殊的 __init__.py 文件的目录,该文件告诉 Python 应该将此目录视为一个包。
创建包目录结构
首先,让我们为我们的包创建必要的目录:
mkdir -p ~/project/mypackage/data
此命令创建一个名为 mypackage 的目录,其中包含一个子目录 data,用于存储我们的附加文件。
现在,让我们导航到我们的项目目录:
cd ~/project
创建基本的包文件
每个 Python 包都需要在其根目录中有一个 __init__.py 文件。让我们创建这个文件:
touch mypackage/__init__.py
这个空文件告诉 Python mypackage 目录是一个包。
接下来,让我们在我们的包中创建一个简单的 Python 模块:
echo 'def greet():
print("Hello from mypackage!")' > mypackage/greeting.py
将数据文件添加到包中
现在,让我们将一个数据文件添加到我们的包中。这可以是一个配置文件、一个 CSV 文件或你的包需要的任何其他类型的文件:
echo 'This is sample data for our package.' > mypackage/data/sample.txt
我们还创建一个配置文件:
echo '[config]
debug = true
log_level = INFO' > mypackage/config.ini
验证你的包结构
你可以使用以下命令检查你的包的结构:
find mypackage -type f | sort
你应该看到类似如下的输出:
mypackage/__init__.py
mypackage/config.ini
mypackage/data/sample.txt
mypackage/greeting.py
这是一个基本的 Python 包结构,其中包含一些额外的非 Python 文件。在接下来的步骤中,我们将学习如何在分发包时包含这些文件,以及如何从你的代码中访问它们。
为你的包创建安装脚本
为了在你的 Python 包中正确地包含附加文件,你需要创建一个 setup.py 文件。Python 的打包工具使用此文件来构建和安装你的包。
理解 setup.py
setup.py 文件包含有关你的包的元数据,例如它的名称、版本、作者和依赖项。它还指定了在分发包时应该包含哪些文件。
让我们在你的项目的根目录中创建一个基本的 setup.py 文件:
cd ~/project
现在,使用以下内容创建 setup.py 文件:
cat > setup.py << 'EOF'
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1",
packages=find_packages(),
## Include data files
package_data={
"mypackage": ["config.ini", "data/*.txt"],
},
## Metadata
author="Your Name",
author_email="your.email@example.com",
description="A simple Python package with additional files",
)
EOF
理解 Package Data 配置
package_data 参数是包含包中附加文件的关键。它接受一个字典,其中:
- 键是包名(或 "" 代表所有包)
- 值是相对于包目录的文件模式列表
在我们的示例中,我们包含:
- 位于我们包根目录的
config.ini文件 data目录中的所有.txt文件
文件模式支持通配符,例如 *,以匹配具有相似名称或扩展名的多个文件。
测试你的安装配置
让我们创建一个虚拟环境来测试我们的包:
python3 -m venv ~/project/venv
source ~/project/venv/bin/activate
现在,让我们以开发模式安装我们的包:
cd ~/project
pip install -e .
-e 标志代表“可编辑”模式,这意味着你可以编辑你的包代码,而无需每次都重新安装它。
你应该看到输出,表明你的包已成功安装:
Successfully installed mypackage-0.1
让我们验证我们的包安装:
python -c "import mypackage.greeting; mypackage.greeting.greet()"
这应该输出:
Hello from mypackage!
你现在已经成功创建了一个 Python 包,该包具有一个包含附加文件的安装脚本。在下一步中,我们将学习如何从你的 Python 代码中访问这些文件。
访问你的包中的附加文件
现在我们已经在包中包含了附加文件,我们需要学习如何从我们的 Python 代码中访问它们。有几种方法可以做到这一点,但最可靠的方法是使用来自 setuptools 包的 pkg_resources 模块。
创建一个模块来访问附加文件
让我们在我们的包中创建一个新模块,演示如何访问附加文件:
cd ~/project
在 mypackage 目录中创建一个名为 fileaccess.py 的新文件:
cat > mypackage/fileaccess.py << 'EOF'
import os
import pkg_resources
def get_config_path():
"""返回 config.ini 文件的路径。"""
return pkg_resources.resource_filename('mypackage', 'config.ini')
def read_config():
"""读取并返回 config.ini 文件的内容。"""
config_path = get_config_path()
with open(config_path, 'r') as f:
return f.read()
def get_sample_data_path():
"""返回 sample.txt 文件的路径。"""
return pkg_resources.resource_filename('mypackage', 'data/sample.txt')
def read_sample_data():
"""读取并返回 sample.txt 文件的内容。"""
data_path = get_sample_data_path()
with open(data_path, 'r') as f:
return f.read()
def list_package_data():
"""列出包含在包数据中的所有文件。"""
## 获取包目录
package_dir = os.path.dirname(pkg_resources.resource_filename('mypackage', '__init__.py'))
## 列出主包目录中的文件
main_files = [f for f in os.listdir(package_dir)
if os.path.isfile(os.path.join(package_dir, f))]
## 列出数据目录中的文件
data_dir = os.path.join(package_dir, 'data')
data_files = [f'data/{f}' for f in os.listdir(data_dir)
if os.path.isfile(os.path.join(data_dir, f))]
return main_files + data_files
EOF
更新 init.py 文件
让我们更新 __init__.py 文件以公开我们的新函数:
cat > mypackage/__init__.py << 'EOF'
from mypackage.greeting import greet
from mypackage.fileaccess import (
get_config_path,
read_config,
get_sample_data_path,
read_sample_data,
list_package_data
)
__all__ = [
'greet',
'get_config_path',
'read_config',
'get_sample_data_path',
'read_sample_data',
'list_package_data'
]
EOF
测试文件访问函数
让我们创建一个脚本来测试我们的文件访问函数:
cat > ~/project/test_package.py << 'EOF'
import mypackage
## 测试问候函数
print("测试问候函数:")
mypackage.greet()
print()
## 测试配置文件访问
print("配置文件路径:")
print(mypackage.get_config_path())
print("\n配置文件内容:")
print(mypackage.read_config())
print()
## 测试数据文件访问
print("样本数据文件路径:")
print(mypackage.get_sample_data_path())
print("\n样本数据文件内容:")
print(mypackage.read_sample_data())
print()
## 列出所有包数据
print("所有包数据文件:")
for file in mypackage.list_package_data():
print(f"- {file}")
EOF
现在运行测试脚本:
cd ~/project
python test_package.py
你应该看到类似如下的输出:
测试问候函数:
Hello from mypackage!
配置文件路径:
/home/labex/project/mypackage/config.ini
配置文件内容:
[config]
debug = true
log_level = INFO
样本数据文件路径:
/home/labex/project/mypackage/data/sample.txt
样本数据文件内容:
This is sample data for our package.
所有包数据文件:
- __init__.py
- config.ini
- fileaccess.py
- greeting.py
- data/sample.txt
理解 pkg_resources
pkg_resources 模块提供了一种访问已安装包内资源的方法。resource_filename 函数返回包内文件的路径,无论包安装在哪里。
这种方法确保你的代码可以访问附加文件,无论:
- 在开发期间从源目录运行
- 安装在虚拟环境中
- 系统范围安装
- 分发并在另一台机器上安装
这使得你的包更具可移植性和可靠性,因为它不依赖于硬编码路径或相对路径,这些路径可能会根据包的使用方式而改变。
构建和分发你的包
现在我们已经创建了一个带有附加文件的 Python 包,并确认我们可以访问它们,让我们学习如何构建和分发这个包。
更新安装脚本
在构建包之前,让我们更新我们的 setup.py 文件以包含更多元数据和需求:
cd ~/project
cat > setup.py << 'EOF'
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1.0",
packages=find_packages(),
## 包含数据文件
package_data={
"mypackage": ["config.ini", "data/*.txt"],
},
## 依赖项
install_requires=[
"setuptools",
],
## 元数据
author="Your Name",
author_email="your.email@example.com",
description="一个带有附加文件的简单 Python 包",
keywords="sample, package, data",
url="https://example.com/mypackage",
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
python_requires=">=3.6",
)
EOF
构建源分发和 Wheel 分发
Python 包可以以多种格式分发,但最常见的是:
- **源分发 (sdist)**:包含源代码和附加文件的 tarball
- **Wheel 分发 (bdist_wheel)**:一个预构建的包,无需构建即可安装
让我们创建这两种类型的分发:
## 确保我们有最新的构建工具
pip install --upgrade setuptools wheel
## 构建分发
python setup.py sdist bdist_wheel
你应该看到输出,表明分发已创建,并且新文件应该出现在 dist 目录中。
让我们检查 dist 目录的内容:
ls -l dist
你应该至少看到两个文件:
- 一个
.tar.gz文件(源分发) - 一个
.whl文件(wheel 分发)
从分发文件安装包
现在,让我们测试从其中一个分发文件安装包。首先,让我们卸载我们的开发版本:
pip uninstall -y mypackage
现在,让我们安装 wheel 分发:
pip install dist/mypackage-0.1.0-py3-none-any.whl
你应该看到输出,表明该包已成功安装。
让我们验证该包是否已安装,并且我们仍然可以访问附加文件:
python -c "import mypackage; print(mypackage.read_config())"
这应该输出 config.ini 文件的内容:
[config]
debug = true
log_level = INFO
发布你的包
在实际场景中,你通常会将你的包发布到 Python 包索引 (PyPI),以便其他人可以使用 pip install mypackage 安装它。这包括:
- 在 PyPI 上创建一个帐户 (https://pypi.org/)
- 使用
twine等工具上传你的分发:pip install twine twine upload dist/*
但是,对于这个实验,我们将停留在本地创建分发。你现在拥有一个完整的 Python 包,其中包含附加文件,可以由其他人分发和安装。
你创建的总结
- 一个带有模块和附加文件的 Python 包
- 一个安装脚本,将这些文件包含在分发中
- 从你的代码访问这些文件的函数
- 准备分发的源分发和 wheel 分发文件
这种结构为将来可能要创建的任何 Python 包提供了坚实的基础。
总结
在这个实验中,你已经学习了如何:
- 创建一个基本的 Python 包结构,其中包含额外的非 Python 文件
- 配置你的
setup.py以将这些文件包含在包分发中 - 使用
pkg_resources模块从你的 Python 代码访问附加文件 - 为分发构建你的包的源分发和 wheel 分发
你现在已经掌握了创建更全面的 Python 包的知识,这些包不仅包含 Python 代码,还包含配置文件、数据文件、模板和其他资源。这种能力对于开发实际应用至关重要,在实际应用中,Python 代码通常需要与外部文件一起工作。
这个实验的一些关键要点:
- 在
setup()中使用package_data参数来包含附加文件 - 使用
pkg_resources.resource_filename()从你的代码可靠地访问这些文件 - 构建源分发和 wheel 分发以实现最大的兼容性
- 保持你的包结构井井有条,以使维护更容易
这些知识在你继续开发更复杂的 Python 应用程序和包时将非常有用。



