管理Python项目中的资源

时间:2009-09-08 18:37:40

标签: python resources setuptools distutils decoupling

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

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

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

4 个答案:

答案 0 :(得分:47)

您可能希望使用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')

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

['__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之类的内容无法解决。

答案 1 :(得分:4)

您可以在每个需要它的子包中始终有一个单独的“resources”文件夹,并使用os.path函数从子包的__file__值中获取这些文件夹。为了说明我的意思,我在三个位置创建了以下__init__.py文件:

c:\temp\topp        (top-level package)
c:\temp\topp\sub1   (subpackage 1)
c:\temp\topp\sub2   (subpackage 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
Traceback (most recent call last):
  File "C:\temp\work\topapp.py", line 1, in 
    import topp
ImportError: No module named topp

这是预期的。我们设置PYTHONPATH来模拟我们的包裹在路径上:

C:\temp\work>set PYTHONPATH=c:\temp

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

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

更新: Here是相关的py2exe文档。

答案 2 :(得分:2)

执行此操作的新方法是使用importlib。对于3.7之前的Python版本,您可以向lambda添加依赖项,并执行类似的操作

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

答案 3 :(得分:1)

@ pycon2009,有一个关于distutils和setuptools的演示文稿。你可以在这里找到所有的视频

Eggs and Buildout Deployment in Python - Part 1

Eggs and Buildout Deployment in Python - Part 2

Eggs and Buildout Deployment in Python - Part 3

在这些视频中,它们描述了如何在包中包含静态资源。我相信它在第2部分。

使用setuptools,您可以定义依赖项,这将允许您拥有2个使用第3个包中的资源的包。

Setuptools还为您提供了访问这些资源的标准方法,并允许您在包中使用相对路径,从而无需担心软件包的安装位置。