147

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

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

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

有什么建议么?

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

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

我正在尝试data.txtmodule*.py!

4

6 回答 6

191

执行此操作的标准方法是使用 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')
于 2011-04-08T23:42:39.203 回答
25

您可以使用__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()
于 2009-04-22T22:37:04.197 回答
21

回答详细说明无法按原样工作的代码通常没有意义,但我认为这是一个例外。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__.py然后data将其用作包装,否则无法解决此问题:

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

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

于 2019-11-19T14:22:00.987 回答
17

提供今天有效的解决方案。绝对使用这个 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 发现包和资源访问

于 2014-10-09T12:33:03.800 回答
8

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

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

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

于 2015-12-10T09:59:33.883 回答
5

我想我找到了答案。

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

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

然后我打开我所有的文件

open(os.path.join(data_path,'filename'), <param>)
于 2009-04-22T22:35:52.187 回答