【问题标题】:Managing resources in a Python project在 Python 项目中管理资源
【发布时间】:2010-11-26 14:32:12
【问题描述】:

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

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

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

【问题讨论】:

    标签: python resources setuptools distutils decoupling


    【解决方案1】:

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

    【讨论】:

    • 从 3.9 开始,这似乎是 importlib.resources.files(package) 现在 (docs)。
    【解决方案2】:

    @ 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 还为您提供了访问这些资源的标准方法,并允许您在包内使用相对路径,这样就无需担心包的安装位置。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 好吧,这里的缺点太多了。用 Python 项目打包资源难道不是一种简单的方法吗?
      • 我只知道 2 种广泛支持的方式(不幸的是,它们并不简单):1)distutils-way(标准):文档将访问资源文件作为练习留给读者(可能因为他们认为相对于__file__ 的路径操作都是一种需要)。 2) setuptools-way(distutils 的超集),如上所述。
      • 很惊讶这还没有被提出,但它不应该是decode 而不是encode 对于resource_string 的输出?
      • @archeezee 这篇文章来自 Python 2.x 时代。 resource_string 在这种情况下返回 pic2.png 的原始字节表示,如果你想打印它,这并不理想:) 因此 base64 编码调用。
      【解决方案4】:

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

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

      更新:Here 的相关 py2exe 文档。

      【讨论】:

      • 但是当你想要 py2exe 整个事情的时候呢?
      • 我不是在谈论如何用代码打包资源。我说的是__file__ 不起作用。
      猜你喜欢
      • 1970-01-01
      • 2021-05-13
      • 2018-08-05
      • 2012-05-03
      • 1970-01-01
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-03-25
      相关资源
      最近更新 更多