package_data 用于将文件打包进 Python 包内部,运行时通过 importlib.resources 等安全读取;data_files 则按系统路径单独安装,适用于全局资源。关键区别在于用途与安装位置。

setup.py 里用 package_data 还是 data_files?
关键看文件用途和安装路径:package_data 只打包进 Python 包内部(跟 .py 文件同级或子目录),运行时用 importlib.resources 或 pkg_resources 读取;data_files 则按系统约定路径(如 /usr/local/share/ 或 %PROGRAMFILES%)单独安装,适合全局配置、图标、文档等。
常见错误是把本该随包分发的 config.yaml 放进 data_files,结果在虚拟环境里找不到路径,或者跨平台行为不一致。
-
package_data必须配合find_packages()或显式声明packages,否则会被忽略 -
data_files的目标路径在 Windows 和 Linux 上语义不同,比如('etc', ['conf/app.conf'])在 Linux 会装到/etc/app.conf,Windows 下可能直接失败或乱放 - 如果用
setuptools61.0+,优先用include_package_data=True+MANIFEST.in,更直观
MANIFEST.in 怎么写才不会漏文件?
MANIFEST.in 控制源码分发包(sdist)里包含哪些非 Python 文件,但它不影响 wheel 构建——wheel 依赖 setup.py 中的 package_data 或 data_files 配置。漏文件最常发生在:只写了 MANIFEST.in 却没配 package_data,导致 pip install wheel 时资源消失。
- 用
recursive-include mypackage *.json *.yaml比include mypackage/conf/*.yaml更可靠,避免路径拼写错误 - 别用
global-include,它会把.gitignore里的文件也塞进去(比如.env) - 执行
python setup.py sdist --dry-run可预览实际打进 tar.gz 的文件列表
运行时怎么安全读取打包进来的配置文件?
硬编码相对路径(如 open('conf/app.yaml'))在开发时能跑,打包后必然失败。Python 3.9+ 推荐用 importlib.resources.files(),3.7–3.8 用 importlib.resources.open_text(),兼容旧版可用 pkg_resources.resource_string()(但已弃用)。
创建 Flask + Vue.js 全栈项目,采用单端口架构、uv 依赖管理、结构化日志和生产级前端设计。当用户要求以下操作时触发:创建新的 Flask+Vue 项目、搭建 Web 应用、构建全栈 Python+Vue 应用、初始化 Flask Web 项目、设置单端口 Flask 前后端项目。触发词:新建项目、创建项目、搭建项目、Flask项目、Vue项目。
from importlib import resources
# Python 3.9+
with resources.files('mypackage').joinpath('conf/app.yaml').open('r') as f:
config = yaml.safe_load(f)
- 不要用
__file__拼路径,__file__在 zipimport(如某些 wheel)下是无效路径 - 如果文件是二进制(如字体、图片),用
.read_bytes()而不是.open() -
resources.files()返回的是Traversable对象,不能直接传给open(),必须调用.open()或.read_text()
PyInstaller 打包时静态资源总找不到?
PyInstaller 默认只扫描 import 语句,不会自动收集 MANIFEST.in 或 package_data 里的文件。必须显式告诉它:“这些路径下的文件也要打进最终可执行文件”。
- 用
--add-data 'src/conf;conf'(Windows)或--add-data 'src/conf:conf'(Linux/macOS),注意分隔符不同 - 代码里读取时,要适配 PyInstaller 的
_MEIPASS临时解压路径:os.path.join(sys._MEIPASS, 'conf', 'app.yaml') - 更稳妥的做法是封装一个函数统一处理路径,开发时走普通路径,打包后自动切到
_MEIPASS
真正麻烦的不是怎么加,而是加完忘了改读取逻辑——资源进了 bundle,但代码还在原路径找,报 FileNotFoundError 时容易误判为打包失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










