介绍
Python 包是组织和分发代码的强大方式。虽然 Python 脚本(.py 文件)构成了包的核心,但你通常需要包含其他文件,例如配置文件、数据文件、模板或文档。本教程将指导你创建一个包含这些附加资源的 Python 包,使你的包更通用、更有用。
在本实验结束时,你将创建一个包含附加文件的完整 Python 包,并学习如何从你的代码中访问这些文件。
Python 包是组织和分发代码的强大方式。虽然 Python 脚本(.py 文件)构成了包的核心,但你通常需要包含其他文件,例如配置文件、数据文件、模板或文档。本教程将指导你创建一个包含这些附加资源的 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 文件:
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 参数是包含包中附加文件的关键。它接受一个字典,其中:
在我们的示例中,我们包含:
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 文件以公开我们的新函数:
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 模块提供了一种访问已安装包内资源的方法。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
Python 包可以以多种格式分发,但最常见的是:
让我们创建这两种类型的分发:
## 确保我们有最新的构建工具
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 安装它。这包括:
twine 等工具上传你的分发:pip install twine
twine upload dist/*
但是,对于这个实验,我们将停留在本地创建分发。你现在拥有一个完整的 Python 包,其中包含附加文件,可以由其他人分发和安装。
这种结构为将来可能要创建的任何 Python 包提供了坚实的基础。
在这个实验中,你已经学习了如何:
setup.py 以将这些文件包含在包分发中pkg_resources 模块从你的 Python 代码访问附加文件你现在已经掌握了创建更全面的 Python 包的知识,这些包不仅包含 Python 代码,还包含配置文件、数据文件、模板和其他资源。这种能力对于开发实际应用至关重要,在实际应用中,Python 代码通常需要与外部文件一起工作。
这个实验的一些关键要点:
setup() 中使用 package_data 参数来包含附加文件pkg_resources.resource_filename() 从你的代码可靠地访问这些文件这些知识在你继续开发更复杂的 Python 应用程序和包时将非常有用。