包子目录中的Python访问数据

Jac*_*les 115 python packages

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

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

这似乎应该是一个微不足道的常见问题.但我似乎无法弄明白.部分问题是我的数据文件不是.py文件,所以我不能使用导入功能等.

有什么建议?

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

/
__init__.py
module1.py
module2.py
data/   
   data.txt
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

ell*_*t42 152

执行此操作的标准方法是使用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')
Run Code Online (Sandbox Code Playgroud)

  • 在python 3.7中,为此,importlib.resources替换了pkg_resources(由于性能问题)。 (8认同)
  • 不会*pkg_resources*在*setuptools*上创建运行时依赖项吗?例如,我重新分发Debian软件包,为什么我会依赖于`python-setuptools`呢?到目前为止,`__ file__`对我来说很好. (6认同)
  • 如果没有安装包,这将如何使用?我只是在本地测试 (5认同)
  • 为什么这样做更好:ResourceManager类提供对包资源的统一访问,无论这些资源是作为文件和目录存在还是在某种存档中压缩 (4认同)
  • 非常好的建议,谢谢.我使用`from pkg_resources import resource_filename open(resource_filename('data','data.txt'),'rb')实现了一个打开的标准文件. (4认同)

Ric*_*dle 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()
Run Code Online (Sandbox Code Playgroud)

  • 如果文件位于分发版(IE.egg)中,则无效.使用pkg_resources获取数据文件. (35认同)
  • 确实,这个坏了。 (3认同)
  • 此外,`__file__` 不适用于 py2exe,因为该值将是 zip 文件的路径。 (2认同)
  • 这在分发(鸡蛋等)的情况下不起作用。 (2认同)

Ant*_*ala 18

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

foo/
    __init__.py
    module1.py
    module2.py
    data/   
       data.txt
    data2.txt
Run Code Online (Sandbox Code Playgroud)

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

importlib.resources.open_binary('foo', 'data2.txt')
Run Code Online (Sandbox Code Playgroud)

但它会失败,但有一个例外

>>> 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
Run Code Online (Sandbox Code Playgroud)

这不能被固定,除了通过将__init__.pydata再使用它作为一个包:

importlib.resources.open_binary('foo.data', 'data.txt')
Run Code Online (Sandbox Code Playgroud)

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

  • 该设计现已更改为可遍历的 API(在 stdlib Python 3.9+ 中可用)。更多详细信息请参阅这里的欺骗 -&gt; /sf/answers/4125907551/ (3认同)

Sas*_*ied 12

To provide a solution working today. Definitely use this API to not reinvent all those wheels.

A true filesystem filename is needed. Zipped eggs will be extracted to a cache directory:

from pkg_resources import resource_filename, Requirement

path_to_vik_logo = resource_filename(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")
Run Code Online (Sandbox Code Playgroud)

Return a readable file-like object for the specified resource; it may be an actual file, a StringIO, or some similar object. The stream is in “binary mode”, in the sense that whatever bytes are in the resource will be read as-is.

from pkg_resources import resource_stream, Requirement

vik_logo_as_stream = resource_stream(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")
Run Code Online (Sandbox Code Playgroud)

Package Discovery and Resource Access using pkg_resources


Jac*_*les 6

我想我已经找到了答案.

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

data_path = os.path.join(os.path.dirname(__file__),'data')
Run Code Online (Sandbox Code Playgroud)

然后我打开我的所有文件

open(os.path.join(data_path,'filename'), <param>)
Run Code Online (Sandbox Code Playgroud)

  • 当资源在存档分发中(例如压缩鸡蛋)时,这将无法工作。喜欢这样的东西:`pkg_resources.resource_string('pkg_name', 'data/file.txt')` (2认同)

Tho*_*ner 6

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

import pkg_resources
print(    
    pkg_resources.resource_filename(__name__, 'data/data.txt')
)
Run Code Online (Sandbox Code Playgroud)

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