Python打包时怎么包含图片、配置文件等资源文件

晚来风
晚来风
发布时间:2026-11-01| 6人浏览过
原创

详细解析Python在两种主流打包场景(分发Wheel包与生成PyInstaller独立可执行程序)下如何正确包含图片、配置文件等非代码资源,以及运行期的标准读取方法与避坑技巧。

在 Python 开发中,包含图片、YAML/JSON 配置、图标、HTML 模板等非代码资源文件,核心在于明确目标打包场景:一种是将项目打包为供他人 pip install 的模块或分发库(Wheel/Sdist);另一种是将程序打包为无需 Python 环境即可独立运行的可执行程序(如 PyInstaller 生成的 .exe 或二进制单文件)。两种场景所依赖的构建配置机制与运行期路径解析完全不同,本文将分别介绍它们的标准处理方式与正确访问方法。

Python打包时怎么包含图片、配置文件等资源文件
图 1:Python 资源文件打包的两大技术路径对比与选型

一、制作 Python 分发包(Wheel)时包含资源文件

当需要把项目发布给其他开发者安装使用时,推荐遵循现代化 PEP 621 标准,在 pyproject.toml 中统一声明构建元数据与数据文件规则。

1. 规范的项目目录布局

资源文件必须位于 Python 包目录内部(即包含 __init__.py 的文件夹内),而非与包平级的根目录下。如果资源处于包外部,标准构建工具默认不会将其归入轮子包内。

my_app/
├── pyproject.toml
├── README.md
└── my_app/
    ├── __init__.py
    ├── core.py
    └── assets/
        ├── __init__.py          # 建议保留,便于按子包定位
        ├── config.yaml          # 配置文件
        └── logo.png             # 图片资源

2. 在 pyproject.toml 中声明包数据

以最通用的 setuptools 构建后端为例,配置 [tool.setuptools.package-data] 明确告诉构建系统需要打包哪些文件类型:

[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "my_app"
version = "0.1.0"
description = "包含静态资源示例应用"
readme = "README.md"
requires-python = ">=3.9"

[tool.setuptools.packages.find]
where = ["."]

# 关键配置:指定包内部需要额外打包的静态文件
[tool.setuptools.package-data]
"my_app.assets" = ["*.yaml", "*.png", "*.json"]
提示:如果不希望逐个后缀罗列,也可以声明 "my_app.assets" = ["*"] 包含该目录下的所有文件。若使用 hatchling 作为构建后端,可在 [tool.hatch.build.targets.wheel] 下配置 packages = ["my_app"],hatchling 默认会包含包目录下的所有非 Python 文件。

3. 正确读取已安装包中的资源文件

初学者容易写出类似 open("assets/config.yaml") 或根据 os.getcwd() 拼路径的代码。一旦其他人通过 pip 安装,并在任意工作目录下调用时,相对路径就会抛出 FileNotFoundError。

在 Python 3.9+ 中,推荐使用内置的 importlib.resources 模块进行资源访问:

from importlib import resources
import yaml

def load_app_config():
    # 获取资源所在路径对象(Traversable 对象)
    config_ref = resources.files("my_app.assets").joinpath("config.yaml")
    
    # 直接以文本方式读取内容
    content = config_ref.read_text(encoding="utf-8")
    return yaml.safe_load(content)

def get_logo_bytes():
    # 直接以二进制方式读取图片数据
    image_ref = resources.files("my_app.assets").joinpath("logo.png")
    return image_ref.read_bytes()
Python打包时怎么包含图片、配置文件等资源文件
图 2:分发 Python 库时包含资源的标准操作流水线

二、使用 PyInstaller 打包独立程序时包含资源

当使用 PyInstaller 将 Python 脚本打包为单文件可执行程序(--onefile)时,PyInstaller 只会自动解析 Python 源码中的 import 引用,不会主动捕获文本和图片资源,必须通过显式规则打包。

1. 命令行 --add-data 参数说明

--add-data 的参数格式为 源文件路径:目标子目录。需要特别注意操作系统的语法差异:

  • Linux 与 macOS(POSIX 环境):使用半角冒号 : 分隔。
  • Windows(CMD / PowerShell 环境):使用分号 ; 分隔。
# Linux / macOS 终端执行命令:
pyinstaller --noconfirm --onedir --windowed \
  --add-data "my_app/assets:my_app/assets" \
  main.py

# Windows 终端执行命令(注意是分号):
pyinstaller --noconfirm --onedir --windowed ^
  --add-data "my_app/assets;my_app/assets" ^
  main.py

2. 在 .spec 文件中持久化配置

若配置项较多,推荐使用 .spec 规格文件管理。在 Analysis 对象的 datas 列表中增加元组定义:

# main.spec
a = Analysis(
    ['main.py'],
    pathex=[],
    binaries=[],
    # datas 列表中每一项为 (本地物理源路径, 打包后的内部目标文件夹)
    datas=[
        ('my_app/assets', 'my_app/assets')
    ],
    hiddenimports=[],
    ...
)

3. PyInstaller 单文件运行原理与路径定位兼容函数

在单文件(--onefile)模式下,程序启动时,PyInstaller 会在操作系统的临时文件夹(如 /tmp 或 C:\Users\...\AppData\Local\Temp)下创建一个名为 _MEIxxxxxx 的临时文件夹,并将所有内嵌资源解压进去。此时:

  • sys._MEIPASS 属性指向该临时解压目录。
  • 在未打包的纯 Python 开发环境下,sys._MEIPASS 并不存在。

因此,项目中的通用路径解析函数必须具备动态自适应能力:

import sys
from pathlib import Path

def get_resource_path(relative_path: str) -> Path:
    """
    获取资源文件的绝对路径。
    自动兼容:
    1. PyInstaller 打包后的运行环境(解压在 sys._MEIPASS)
    2. 本地开发调试阶段的 Python 环境
    """
    if hasattr(sys, "_MEIPASS"):
        # 打包后运行:指向临时解压目录
        base_path = Path(sys._MEIPASS)
    else:
        # 开发模式:以当前源码根目录或文件所在目录为基准
        base_path = Path(__file__).resolve().parent

    return base_path / relative_path

# 业务使用示例:
config_path = get_resource_path("my_app/assets/config.yaml")
with open(config_path, "r", encoding="utf-8") as f:
    print("成功读取配置:", f.read())
Python打包时怎么包含图片、配置文件等资源文件
图 3:PyInstaller 单文件模式运行时资源路径动态解析逻辑

三、常见打包踩坑与排查指南

现象 / 报错 根本原因 对应解决方案
打包为 Wheel 后,用户 pip install 报 ModuleNotFoundError 或找不到静态资源 静态资源放在了顶层项目根目录下,或者 pyproject.toml 未包含对应的子路径规则。 将静态资源统一移入包目录(如 my_app/assets/),并在 package-data 中精准配置通配符。
Windows 下执行 PyInstaller 报 invalid syntax 或 add-data 语法错误 Windows 控制台使用了冒号 : 作为分隔符,而 Windows 盘符自带冒号导致冲突。 在 Windows 系统中将 --add-data 的分隔符改为分号 ;。
PyInstaller 单文件程序运行时修改配置文件,程序重启后修改丢失 单文件模式每次运行解压在随机的临时目录 _MEIxxxx 中,程序退出后临时目录会被自动销毁。 打包的资源文件应严格视为只读资产。用户自定义可写配置应存放于系统的用户家目录(如 ~/.config/my_app/ 或 platformdirs 获取的路径)。
在子函数中直接调用 open("assets/logo.png") 偶发性报错 直接写相对路径依赖当前进程的当前工作目录(CWD),若用户从其他路径启动程序则无法定位。 始终基于 importlib.resources 或 get_resource_path() 生成绝对路径后再执行文件操作。
Python打包时怎么包含图片、配置文件等资源文件
图 4:资源文件打包排查全流程决策树

四、总结与最佳实践

处理 Python 资源文件打包时,牢记两条核心准则:

  1. 构建期明确定位:制作 Wheel 时,在 pyproject.toml 的 package-data 中声明,并保证资源文件位于 Python 包体内部;制作独立可执行程序时,使用 --add-data(注意不同系统的分隔符)将资源打入单文件内。
  2. 运行期规范读取:摒弃写死相对路径的习惯。库项目使用标准的 importlib.resources.files() 访问;独立程序则使用基于 sys._MEIPASS 的自适应绝对路径解析函数,并将打包资源视为只读文件,确保程序在任何机器与工作目录下都能稳定运行。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。