我有一个 Python 项目,其中使用了许多非代码文件。目前这些都是图像,但我将来可能会使用其他类型的文件。什么是存储和引用这些文件的好方案?
我考虑只是在主目录中创建一个文件夹“资源”,但是有一个问题;我的项目的子包中使用了一些图像。以这种方式存储这些图像会导致耦合,这是一个缺点。
此外,我需要一种访问这些文件的方法,该方法与我当前的目录无关。
我有一个 Python 项目,其中使用了许多非代码文件。目前这些都是图像,但我将来可能会使用其他类型的文件。什么是存储和引用这些文件的好方案?
我考虑只是在主目录中创建一个文件夹“资源”,但是有一个问题;我的项目的子包中使用了一些图像。以这种方式存储这些图像会导致耦合,这是一个缺点。
此外,我需要一种访问这些文件的方法,该方法与我当前的目录无关。
您可能想pkg_resources
使用setuptools
.
例如,我制作了一个快速的小包"proj"
来说明我将使用的资源组织方案:
项目/setup.py 项目/项目/__init__.py 项目/项目/code.py 项目/项目/资源/__init__.py 项目/项目/资源/图像/__init__.py 项目/项目/资源/图像/pic1.png 项目/项目/资源/图像/pic2.png
请注意我如何将所有资源保存在单独的子包中。
"code.py"
显示如何pkg_resources
用于引用资源对象:
from pkg_resources import resource_string, resource_listdir
# Itemize data files under proj/resources/images:
print resource_listdir('proj.resources.images', '')
# Get the data file bytes:
print resource_string('proj.resources.images', 'pic2.png').encode('base64')
如果你运行它,你会得到:
['__init__.py', '__init__.pyc', 'pic1.png', 'pic2.png'] iVBORw0KGgoAAAANSUhE ...
如果您需要将资源视为文件对象,请使用resource_stream()
.
访问资源的代码可以在项目的子包结构中的任何位置proj.resources.images
,在这种情况下,它只需要按全名引用包含图像的子包: 。
这是"setup.py"
:
#!/usr/bin/env python
from setuptools import setup, find_packages
setup(name='proj',
packages=find_packages(),
package_data={'': ['*.png']})
警告:
要在“本地”测试事物,即先不安装软件包,您必须从具有setup.py
. 如果您与 位于同一目录中code.py
,Python 将不知道proj
包。所以像这样的事情proj.resources
不会解决。
您始终可以在每个需要它的子包中拥有一个单独的“资源”文件夹,并使用函数从子包的值中os.path
获取这些。__file__
为了说明我的意思,我__init__.py
在三个位置创建了以下文件:
c:\temp\topp(顶级包) c:\temp\topp\sub1(子包 1) c:\temp\topp\sub2(子包 2)
这是__init__.py
文件:
import os.path
resource_path = os.path.join(os.path.split(__file__)[0], "resources")
print resource_path
在 c:\temp\work 中,我创建了一个 app,topapp.py,如下:
import topp
import topp.sub1
import topp.sub2
这表示使用topp
包和子包的应用程序。然后我运行它:
C:\temp\work>topapp 回溯(最近一次通话最后): 文件“C:\temp\work\topapp.py”,第 1 行,在 进口顶级 ImportError:没有名为 topp 的模块
正如预期的那样。我们设置 PYTHONPATH 来模拟我们的包在路径上:
C:\temp\work>设置 PYTHONPATH=c:\temp C:\temp\work>topapp c:\temp\topp\资源 c:\temp\topp\sub1\resources c:\temp\topp\sub2\resources
如您所见,资源路径正确解析为路径上实际(子)包的位置。
更新: 这是相关的 py2exe 文档。
这样做的新方法是使用importlib
. 对于早于 3.7 的 Python 版本,您可以添加依赖项importlib_resources
并执行类似的操作
from importlib_resources import files
def get_resource(module: str, name: str) -> str:
"""Load a textual resource file."""
return files(module).joinpath(name).read_text(encoding="utf-8")
如果您的资源位于foo/resources
子模块中,那么您将get_resource
像这样使用
resource_text = get_resource('foo.resources', 'myresource')
@ pycon2009,有一个关于 distutils 和 setuptools 的演讲。你可以在这里找到所有的视频
Python 中的 Eggs 和 Buildout 部署 - 第 1 部分
Python 中的 Eggs 和 Buildout 部署 - 第 2 部分
Python 中的 Eggs 和 Buildout 部署 - 第 3 部分
在这些视频中,它们描述了如何在包中包含静态资源。我相信它在第 2 部分。
使用 setuptools,您可以定义依赖关系,这将允许您拥有 2 个使用来自第 3 个包的资源的包。
Setuptools 还为您提供了访问这些资源的标准方法,并允许您在包中使用相对路径,从而无需担心包的安装位置。