访问包子目录中的数据

我正在编写一个 Python 包,其中包含需要在 ./data/子目录中打开数据文件的模块。现在,我已经把文件的路径硬编码到我的类和函数中了。我希望编写更健壮的代码,以便能够访问子目录,而不管它安装在用户系统的哪个位置。

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

这似乎应该是一个微不足道的、常见的问题。但我好像还是想不明白。问题的一部分在于我的数据文件不是 .py文件,所以我不能使用导入函数之类的。

有什么建议吗?

现在我的软件包目录看起来像:

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

我试图从 module*.py访问 data.txt

67650 次浏览

我想我找到了答案。

我创建了一个 data _ path.py 模块,并将其导入到其他模块中,其中包含:

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

然后我打开所有的文件

open(os.path.join(data_path,'filename'), <param>)

您可以使用 __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()

这样做的标准方法是使用 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')

要提供一个解决方案今天工作。一定要使用这个 API,不要重新发明所有这些车轮。

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

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 进行包发现和资源访问

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

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

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

回答详细的 没有代码是否能正常工作通常是没有意义的,但我认为这是一个例外。Python 3.7添加了 importlib.resources,它应该取代 pkg_resources。它可以访问包中名称中没有 斜线的文件,即。

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

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

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

这种行为的原因是 “这是设计好的”; 但是 设计可能会改变..。