管理 Python 项目中的资源

发布于 2024-08-04 08:55:49 字数 199 浏览 7 评论 0原文

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

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

另外,我需要一种独立于当前目录的访问这些文件的方法。

I have a Python project in which I am using many non-code files. Currently these are all images, but I might use other kinds of files in the future. What would be a good scheme for storing and referencing these files?

I considered just making a folder "resources" in the main directory, but there is a problem; Some images are used from within sub-packages of my project. Storing these images that way would lead to coupling, which is a disadvantage.

Also, I need a way to access these files which is independent on what my current directory is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清醇 2024-08-11 08:55:49

您可能需要使用 setuptools 附带的 pkg_resources 库。

例如,我制作了一个快速的小包 "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 这样的事情将无法解决。

You may want to use pkg_resources library that comes with setuptools.

For example, I've made up a quick little package "proj" to illustrate the resource organization scheme I'd use:

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

Notice how I keep all resources in a separate subpackage.

"code.py" shows how pkg_resources is used to refer to the resource objects:

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')

If you run it, you get:

['__init__.py', '__init__.pyc', 'pic1.png', 'pic2.png']
iVBORw0KGgoAAAANSUhE ...

If you need to treat a resource as a fileobject, use resource_stream().

The code accessing the resources may be anywhere within the subpackage structure of your project, it just needs to refer to subpackage containing the images by full name: proj.resources.images, in this case.

Here's "setup.py":

#!/usr/bin/env python

from setuptools import setup, find_packages

setup(name='proj',
      packages=find_packages(),
      package_data={'': ['*.png']})

Caveat:
To test things "locally", that is w/o installing the package first, you'll have to invoke your test scripts from directory that has setup.py. If you're in the same directory as code.py, Python won't know about proj package. So things like proj.resources won't resolve.

独闯女儿国 2024-08-11 08:55:49

执行此操作的新方法是使用 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')

The new way of doing this is with importlib. For Python versions older than 3.7 you can add a dependency to importlib_resources and do something like

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")

If your resources live inside the foo/resources sub-module, you would then use get_resource like so

resource_text = get_resource('foo.resources', 'myresource')
旧情勿念 2024-08-11 08:55:49

您始终可以在每个需要它的子包中拥有一个单独的“资源”文件夹,并使用 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

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

更新: 这里是相关的 py2exe 文档。

You can always have a separate "resources" folder in each subpackage which needs it, and use os.path functions to get to these from the __file__ values of your subpackages. To illustrate what I mean, I created the following __init__.py file in three locations:

c:\temp\topp        (top-level package)
c:\temp\topp\sub1   (subpackage 1)
c:\temp\topp\sub2   (subpackage 2)

Here's the __init__.py file:

import os.path
resource_path = os.path.join(os.path.split(__file__)[0], "resources")
print resource_path

In c:\temp\work, I create an app, topapp.py, as follows:

import topp
import topp.sub1
import topp.sub2

This respresents the application using the topp package and subpackages. Then I run it:

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

That's as expected. We set the PYTHONPATH to simulate having our package on the path:

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

As you can see, the resource paths resolved correctly to the location of the actual (sub)packages on the path.

Update: Here's the relevant py2exe documentation.

我很OK 2024-08-11 08:55:49

@ pycon2009,有一个关于 distutils 和 setuptools 的演示。您可以在此处找到所有视频

Python 中的鸡蛋和构建部署 - 第 1 部分

Python 中的鸡蛋和构建部署 - 第 2 部分

Python 中的鸡蛋和构建部署 - 第 3 部分

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

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

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

@ pycon2009, there was a presentation on distutils and setuptools. You can find all of the videos here

Eggs and Buildout Deployment in Python - Part 1

Eggs and Buildout Deployment in Python - Part 2

Eggs and Buildout Deployment in Python - Part 3

In these videos, they describe how to include static resources in your package. I believe its in part 2.

With setuptools, you can define dependancies, this would allow you to have 2 packages that use resources from 3rd package.

Setuptools also gives you a standard way of accessing these resources and allows you to use relative paths inside of your packages, which eliminates the need to worry about where your packages are installed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文