管理Python项目中的资源

Ram*_*hum 44 python resources distutils setuptools decoupling

我有一个Python项目,其中我使用了许多非代码文件.目前这些都是图像,但我将来可能会使用其他类型的文件.什么是存储和引用这些文件的好方案?

我考虑在主目录中创建一个文件夹"resources",但是有一个问题; 有些图像是从我项目的子包中使用的.以这种方式存储这些图像会导致耦合,这是一个缺点.

另外,我需要一种方法来访问这些文件,这些文件与我当前的目录无关.

Pav*_*pin 52

您可能想要使用pkg_resources附带的库setuptools.

例如,我编写了一个快速的小包"proj"来说明我使用的资源组织方案:

proj/setup.py
proj/proj/__init__.py
proj/proj/code.py
proj/proj/resources/__init__.py
proj/proj/resources/images/__init__.py
proj/proj/resources/images/pic1.png
proj/proj/resources/images/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')
Run Code Online (Sandbox Code Playgroud)

如果你运行它,你会得到:

['__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']})
Run Code Online (Sandbox Code Playgroud)

警告: 为了"本地"测试,即没有先安装软件包,你必须从具有的目录调用测试脚本setup.py.如果你在同一个目录中code.py,Python就不会知道proj包.所以这样的事情proj.resources无法解决.

  • 嗯,这里的缺点太多了.用Python项目打包资源是否有一种理智的简单方法? (6认同)

Vin*_*jip 5

在每个需要它的子包中,您始终可以有一个单独的“ 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
Run Code Online (Sandbox Code Playgroud)

在c:\ temp \ work中,我创建一个应用程序topapp.py,如下所示:

import topp
import topp.sub1
import topp.sub2
Run Code Online (Sandbox Code Playgroud)

这表示使用topp包和子包的应用程序。然后我运行它:

C:\ temp \ work> topapp
追溯(最近一次通话):
  文件“ C:\ temp \ work \ topapp.py”,第1行,在 
    进口topp
ImportError:没有名为topp的模块

符合预期。我们将PYTHONPATH设置为模拟将程序包放在路径上:

C:\ temp \ work>设置PYTHONPATH = c:\ temp

C:\ temp \ work> topapp
c:\ temp \ topp \ resources
c:\ temp \ topp \ sub1 \ resources
c:\ temp \ topp \ sub2 \ resources

如您所见,资源路径正确解析为路径上实际(子)程序包的位置。

更新: 是相关的py2exe文档。


Pho*_*x87 5

这样做的新方法是使用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")
Run Code Online (Sandbox Code Playgroud)

如果您的资源位于foo/resources子模块中,那么您将get_resource像这样使用

resource_text = get_resource('foo.resources', 'myresource')
Run Code Online (Sandbox Code Playgroud)

  • 从 3.9 开始,这似乎是 `importlib.resources.files(package)` ([docs](https://docs.python.org/3/library/importlib.html#importlib.resources.files))。 (2认同)