1. 简介
这个模块使得Python的导入系统提供了访问*包*内的*资源*的功能。如果能够导入一个包,那么就能够访问那个包里面的资源。资源可以以二进制或文本模式方式被打开或读取。
Python项目使用项目内的资源文件,例如项目所需的数据文件。通常使用以下方法
- 硬编码数据文件的路径
- 将数据文件放入包装中,并使用进行定位
__file__
- 使用
setuptools.pkg_resources
访问数据文件资源
如 项目文件结构如下, 在test.py 中要使用data.txt 中的数据
myproject/
├── package/
│ ├── __init__.py
│ ├── tool.py
│ ├── data.txt
├── test.py
1.1 硬编码文件路径
使用最直接的方法, open函数,传入data.txt文件的路径,即可访问文件。
# test.py
with open('package/data.txt', 'r') as f:
text = f.read()
print(text)
问题是, 由于是采用相对路径, 如果在其他路径执行test.py 的代码, (如在父目录中) 就会报错
❯ python ./myproject/test_resource.py
FileNotFoundError: [Errno 2] No such file or directory: 'package/data.txt'
1.2 使用__file__定位,使用绝对路径
一种更加优雅的方式,是使用文件的__file__属性,定位到当前的路径, 结合pathlib,使用绝对路径访问目标文件。
import pathlib
PATH = pathlib.Path(__file__)
data_file = PATH.parent / 'package' /'data.txt'
print(data_file.read_text())
但是也有问题, 如果是一个大包的python应用, 它可能最终位于zip内并且没有__file__
属性。
1.3 setuptools.pkg_resources
使用setuptools.pkg_resources 可以解决上述的问题, 但是缺点是很慢。
1.4 更好的解决方案
使用Python 3.7 引入标准库中的新模块 importlib.resources 可以解决以上问题。
这个模块使得Python的导入系统提供了访问*包*内的*资源*的功能。如果能够导入一个包,那么就能够访问那个包里面的资源。资源可以以二进制或文本模式方式被打开或读取。
代码如下:
import importlib.resources as res
import package
text = res.read_text(package, 'data.txt', encoding='utf-8', errors='strict')
print(text)
2. 函数介绍
① 前面使用了read_text, importlib.resources 还有另一个方法 open_text, 以文本格式打开。 返回一个io对象, 效果同 open('package/data.txt', 'r')
datafile = res.open_text(package, 'data.txt', encoding='utf-8', errors='strict')
print(datafile.read())
② 对应的二进制打开的方法, open_binary 和 read_binary 。
③ 还有一个 contents 方法, 返回包内所有文件对象。
data = res.contents(package)
for con in data:
print(con)
返回
data.txt
tool.py
__init__.py
__pycache__
④ path 方法, 此函数返回一个上下文管理器以用于 with 语句。 上下文管理器提供了一个 pathlib.Path 对象。
with res.path(package, 'data.txt') as datafile:
print(datafile)