访问包子目录中的数据


130

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

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

看来这应该是一个微不足道的普遍问题。但是我似乎无法弄清楚。问题的部分原因是我的数据文件不是.py文件,因此我不能使用导入功能等。

有什么建议?

现在,我的包目录如下所示:

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

我试图访问data.txt距离module*.py


Answers:


24

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

44
如果文件在发行版(IE。egg)中,则将无法使用。使用pkg_resources获取数据文件。
克里斯(Chris)

2
确实,这是坏的。
费德里科

1
另外,__file__不适用于py2exe,因为该值将是zip文件的路径。
波德

1
这实际上为我工作。没有任何问题。我正在使用python 3.6
Jorge

1
这在分发(例如鸡蛋等)的情况下不起作用。
Adarsh Trivedi

166

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

7
pkg_resources不会在setuptools上创建运行时依赖吗?例如,我重新分发了一个Debian软件包,那么为什么我要python-setuptools仅仅依靠它呢?到目前为止,__file__对我来说还不错。
mlt

4
为什么这样更好:ResourceManager类提供对包资源的统一访问权,无论这些资源以文件和目录的形式存在还是在某种存档中被压缩
vrdhn 2013年

4
很棒的建议,谢谢。我实现了使用from pkg_resources import resource_filename open(resource_filename('data', 'data.txt'), 'rb')
eageranalyst 2014年

5
未安装软件包时如何使用该软件包?我的意思是只是在本地测试
Claudiu

11
在python 3.7中,为此importlib.resources替换pkg_resources(由于性能问题)。
吉明

13

提供今天可以使用的解决方案。绝对使用此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进行软件包发现和资源访问


10

做出详细的代码无法按原样工作的答案通常是没有意义的,但是我认为这是一个例外。Python 3.7添加importlib.resources了应该替换的pkg_resources。它可以用于访问名称中没有斜杠的软件包中的文件,即

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__.pydata再使用它作为一个包:

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

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


您是否有比youtube视频更好的“设计使然链接(最好是带有文字的链接)?
Gerrit

@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?"
Antti Haapala

8

您需要为整个模块命名,目录树没有列出详细信息,对我来说这是可行的:

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的主要目标之一。本着这种精神,本文档中的所有路径名均以斜杠分隔。
Changyuheng

6

我想我找到了答案。

我创建一个模块data_path.py,将其导入其他包含以下内容的模块:

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

然后我用打开所有文件

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

2
当资源位于存档分发中(例如压缩的鸡蛋)时,这将无法工作。更喜欢这样的东西:pkg_resources.resource_string('pkg_name', 'data/file.txt')
ankostis 2014年

如果@ankostis setuptools检测到您在__file__某处使用过,则足以提取存档。就我而言,我使用的是一个真正需要路径而不是流的库。当然,我可以将文件临时写入磁盘,但是很懒,我只是使用setuptools的功能。
letmaik 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.