我正在编写一个包含需要在./data/
子目录中打开数据文件的模块的python包。现在我有了硬编码到我的类和函数中的文件的路径。我想编写更强大的代码,可以访问子目录,无论它在用户系统上的安装位置如何。
我尝试了各种各样的方法,但到目前为止我没有运气。似乎大多数“当前目录”命令都返回系统的python解释器的目录,而不是模块的目录。
这似乎应该是一个微不足道的常见问题。但我似乎无法弄明白。部分问题是我的数据文件不是.py
文件,所以我不能使用导入功能等。
有什么建议吗?
现在我的包目录如下:
/
__init__.py
module1.py
module2.py
data/
data.txt
我正在尝试从data.txt
module*.py
谢谢!
答案 0 :(得分:137)
执行此操作的标准方法是使用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')
答案 1 :(得分:23)
您可以使用__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()
答案 2 :(得分:12)
提供今天的解决方案。绝对使用这个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
进行包发现和资源访问答案 3 :(得分:6)
我想我已经找到了答案。
我创建了一个模块data_path.py,我将其导入到包含以下内容的其他模块中:
data_path = os.path.join(os.path.dirname(__file__),'data')
然后我用
打开所有文件open(os.path.join(data_path,'filename'), <param>)
答案 4 :(得分:6)
你需要一个整个模块的名称,你得到的目录树没有列出那个细节,对我来说这很有用:
import pkg_resources
print(
pkg_resources.resource_filename(__name__, 'data/data.txt')
)
值得注意的是,setuptools似乎无法根据与打包数据文件的名称匹配来解析文件,所以无论如何你都必须包含data/
前缀。如果需要备用目录分隔符,可以使用os.path.join('data', 'data.txt)
。通常,我发现硬编码的unix样式目录分隔符没有兼容性问题。
答案 5 :(得分:3)
做出答案通常没有意义,因为它详细说明了不能起作用的代码,但是我认为这是一个例外。 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')
此行为的原因是"it is by design";但是the design might change ...