访问包子目录中的数据[重复]

Posted

技术标签:

【中文标题】访问包子目录中的数据[重复]【英文标题】:Access data in package subdirectory [duplicate] 【发布时间】:2010-10-21 05:43:52 【问题描述】:

我正在编写一个 python 包,其中包含需要在 ./data/ 子目录中打开数据文件的模块。现在我有硬编码到我的类和函数中的文件的路径。我想编写更健壮的代码,无论它安装在用户系统的哪个位置,都可以访问子目录。

我尝试了多种方法,但到目前为止我都没有运气。似乎大多数“当前目录”命令返回系统的python解释器的目录,而不是模块的目录。

这似乎应该是一个微不足道的常见问题。然而我似乎无法弄清楚。部分问题是我的数据文件不是.py 文件,所以我不能使用导入函数等。

有什么建议吗?

现在我的包目录如下:

/
__init__.py
module1.py
module2.py
data/   
   data.txt

我正在尝试从module*.py 访问data.txt

【问题讨论】:

【参考方案1】:

执行此操作的标准方法是使用 setuptools 包和 pkg_resources。

您可以根据以下层次结构布局您的包,并配置包设置文件以将其指向您的数据资源,根据此链接:

http://docs.python.org/distutils/setupscript.html#installing-package-data

然后您可以按照此链接使用 pkg_resources 重新查找和使用这些文件:

http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access

import pkg_resources

DATA_PATH = pkg_resources.resource_filename('<package name>', 'data/')
DB_FILE = pkg_resources.resource_filename('<package name>', 'data/sqlite.db')

【讨论】:

不会 pkg_resources 创建对 setuptools 的运行时依赖项吗?例如,我重新分发了一个 Debian 软件包,那么我为什么要为此依赖 python-setuptools 呢?到目前为止__file__ 对我来说很好。 为什么这样更好:ResourceManager 类提供对包资源的统一访问,无论这些资源是作为文件和目录存在还是压缩在某种存档中 绝妙的建议,谢谢。我使用from pkg_resources import resource_filename open(resource_filename('data', 'data.txt'), 'rb') 实现了一个标准文件打开 在未安装软件包时如何使用它?我的意思是在本地测试 在 python 3.7 中,importlib.resources 为此目的替换了pkg_resources(因为性能问题)。【参考方案2】:

您可以使用__file__ 获取包的路径,如下所示:

import os
this_dir, this_filename = os.path.split(__file__)
DATA_PATH = os.path.join(this_dir, "data", "data.txt")
print open(DATA_PATH).read()

【讨论】:

如果文件在发行版中(IE.egg),这将不起作用。使用 pkg_resources 获取数据文件。 确实,这个坏了。 另外,__file__ 不适用于 py2exe,因为该值将是 zip 文件的路径。 这实际上对我有用。没有任何问题。我正在使用 python 3.6 这在分发(鸡蛋等)的情况下不起作用。【参考方案3】:

通常没有必要回答细节代码按原样工作,但我认为这是一个例外。 Python 3.7 添加了应该替换 pkg_resourcesimportlib.resources。它适用于访问名称中没有 slashes 的包中的文件,即

foo/
    __init__.py
    module1.py
    module2.py
    data/   
       data.txt
    data2.txt

即例如,您可以访问 data2.txt 包内的 foo

importlib.resources.open_binary('foo', 'data2.txt')

但它会失败并出现异常

>>> importlib.resources.open_binary('foo', 'data/data.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/importlib/resources.py", line 87, in open_binary
    resource = _normalize_path(resource)
  File "/usr/lib/python3.7/importlib/resources.py", line 61, in _normalize_path
    raise ValueError('!r must be only a file name'.format(path))
ValueError: 'data/data2.txt' must be only a file name

除非将__init__.py 放入data,然后将其作为一个包使用,否则无法解决此问题:

importlib.resources.open_binary('foo.data', 'data.txt')

这种行为的原因是"it is by design";但是the design might change...

【讨论】:

你有没有比 youtube 视频更好的链接“这是设计使然” - 最好是带有文字的? @gerrit 第二个确实包含文本。 "This was a deliberate choice, but I think you have a valid use case. @brettcannon what do you think? And if we allow this, should we make sure it gets into Python 3.7?" 设计现在已更改为可遍历的 API(在 stdlib Python 3.9+ 中可用)。更多细节在欺骗这里-> ***.com/a/58941536/674039【参考方案4】:

提供今天有效的解决方案。绝对不要使用此 API 重新发明所有这些***。

需要一个真正的文件系统文件名。压缩的鸡蛋将被提取到缓存目录:

from pkg_resources import resource_filename, Requirement

path_to_vik_logo = resource_filename(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

为指定的资源返回一个可读的类文件对象;它可能是一个实际的文件、一个 StringIO 或一些类似的对象。流处于“二进制模式”,即资源中的任何字节都将按原样读取。

from pkg_resources import resource_stream, Requirement

vik_logo_as_stream = resource_stream(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

使用 pkg_resources 进行包发现和资源访问

https://setuptools.readthedocs.io/en/latest/pkg_resources.html#resource-extraction https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access

【讨论】:

【参考方案5】:

你需要为你的整个模块命名,给你的目录树没有列出那个细节,对我来说这很有效:

import pkg_resources
print(    
    pkg_resources.resource_filename(__name__, 'data/data.txt')
)

值得注意的是 setuptools 似乎不会根据与打包数据文件的名称匹配来解析文件,所以无论如何你都必须包含 data/ 前缀。如果您需要备用目录分隔符,您可以使用os.path.join('data', 'data.txt),但一般我发现硬编码的 unix 样式目录分隔符没有兼容性问题。

【讨论】:

docs.python.org/3.6/distutils/… > 请注意,安装脚本中提供的任何路径名(文件或目录)都应使用 Unix 约定编写,即斜杠分隔。在实际使用路径名之前,Distutils 将负责将此平台中立表示转换为适合您当前平台的任何内容。这使您的安装脚本可跨操作系统移植,这当然是 Distutils 的主要目标之一。本着这种精神,本文档中的所有路径名都用斜杠分隔。【参考方案6】:

我想我找到了答案。

我制作了一个模块 data_path.py,我将它导入到我的其他模块中,其中包含:

data_path = os.path.join(os.path.dirname(__file__),'data')

然后我用

打开所有文件
open(os.path.join(data_path,'filename'), <param>)

【讨论】:

当资源在存档分发中(例如压缩的鸡蛋)时,这将无法工作。喜欢这样的东西:pkg_resources.resource_string('pkg_name', 'data/file.txt') @ankostis setuptools 足够聪明,可以在检测到您在某处使用 __file__ 时提取存档。就我而言,我使用了一个真正需要路径而不是流的库。当然我可以将文件临时写入磁盘,但我只是使用 setuptools 的功能。

以上是关于访问包子目录中的数据[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何访问测试代码中的测试套件目录路径[重复]

如何获取.java文件的目录[重复]

Java22线程创建(卖票),线程同步(卖包子)

使用 .htaccess 防止通过 /public/ 目录访问 Laravel [重复]

总结

如何将数据集放入 R 包中