我有一个Python项目,其中我使用了许多非代码文件。目前这些都是图像,但我将来可能会使用其他类型的文件。什么是存储和引用这些文件的好方案?
我考虑在主目录中创建一个文件夹“resources”,但是有一个问题;有些图像是从我项目的子包中使用的。以这种方式存储这些图像会导致耦合,这是一个缺点。
另外,我需要一种方法来访问这些文件,这与我当前的目录无关。
答案 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还为您提供了访问这些资源的标准方法,并允许您在包中使用相对路径,从而无需担心软件包的安装位置。