Python `pkgutil.get_data` 扰乱了未来的进口
Python `pkgutil.get_data` disrupts future imports
考虑以下包结构:
.
├── module
│ ├── __init__.py
│ └── submodule
│ ├── attribute.py
│ ├── data.txt
│ └── __init__.py
└── test.py
和下面的一段代码:
import pkgutil
data = pkgutil.get_data('module.submodule', 'data.txt')
import module.submodule.attribute
retval = module.submodule.attribute.hello()
运行 这将引发错误:
Traceback (most recent call last):
File "test.py", line 7, in <module>
retval = module.submodule.attribute.hello()
AttributeError: module 'module' has no attribute 'submodule'
但是,如果您运行以下内容:
import pkgutil
import module.submodule.attribute
data = pkgutil.get_data('module.submodule', 'data.txt')
retval = module.submodule.attribute.hello()
或
import pkgutil
import module.submodule.attribute
retval = module.submodule.attribute.hello()
它工作正常。
为什么 运行ning pkgutil.get_data
会中断未来的导入?
首先,这是一个很好的问题,也是学习有关 python 的导入系统的新知识的好机会。那么让我们开始吧!
如果我们查看 the implementation of pkgutil.get_data
,我们会看到类似这样的内容:
def get_data(package, resource):
spec = importlib.util.find_spec(package)
if spec is None:
return None
loader = spec.loader
if loader is None or not hasattr(loader, 'get_data'):
return None
# XXX needs test
mod = (sys.modules.get(package) or
importlib._bootstrap._load(spec))
if mod is None or not hasattr(mod, '__file__'):
return None
# Modify the resource name to be compatible with the loader.get_data
# signature - an os.path format "filename" starting with the dirname of
# the package's __file__
parts = resource.split('/')
parts.insert(0, os.path.dirname(mod.__file__))
resource_name = os.path.join(*parts)
return loader.get_data(resource_name)
你的问题的答案在这部分代码中:
mod = (sys.modules.get(package) or
importlib._bootstrap._load(spec))
它查看已经加载的包,如果我们正在寻找的包(本例中的 module.submodule
)存在,它会使用它,如果不存在,则尝试使用 [=15= 加载包].
所以让我们看看 implementation of importlib._bootstrap._load
看看发生了什么。
def _load(spec):
"""Return a new module object, loaded by the spec's loader.
The module is not added to its parent.
If a module is already in sys.modules, that existing module gets
clobbered.
"""
with _ModuleLockManager(spec.name):
return _load_unlocked(spec)
嗯,就在那里!文档说“该模块未添加到其 parent。”
表示加载了submodule
模块,但没有添加到module
模块中。因此,当我们尝试通过 module
访问 submodule
时,没有连接,因此 AtrributeError
.
get_data
方法使用这个函数是有意义的,因为它只需要包中的一些其他文件,不需要导入整个包并将其添加到它的 parent及其 parents' parent 等等。
要亲自查看,我建议使用调试器并设置一些断点。然后你就可以一步一步地看到发生了什么。
考虑以下包结构:
.
├── module
│ ├── __init__.py
│ └── submodule
│ ├── attribute.py
│ ├── data.txt
│ └── __init__.py
└── test.py
和下面的一段代码:
import pkgutil
data = pkgutil.get_data('module.submodule', 'data.txt')
import module.submodule.attribute
retval = module.submodule.attribute.hello()
运行 这将引发错误:
Traceback (most recent call last):
File "test.py", line 7, in <module>
retval = module.submodule.attribute.hello()
AttributeError: module 'module' has no attribute 'submodule'
但是,如果您运行以下内容:
import pkgutil
import module.submodule.attribute
data = pkgutil.get_data('module.submodule', 'data.txt')
retval = module.submodule.attribute.hello()
或
import pkgutil
import module.submodule.attribute
retval = module.submodule.attribute.hello()
它工作正常。
为什么 运行ning pkgutil.get_data
会中断未来的导入?
首先,这是一个很好的问题,也是学习有关 python 的导入系统的新知识的好机会。那么让我们开始吧!
如果我们查看 the implementation of pkgutil.get_data
,我们会看到类似这样的内容:
def get_data(package, resource):
spec = importlib.util.find_spec(package)
if spec is None:
return None
loader = spec.loader
if loader is None or not hasattr(loader, 'get_data'):
return None
# XXX needs test
mod = (sys.modules.get(package) or
importlib._bootstrap._load(spec))
if mod is None or not hasattr(mod, '__file__'):
return None
# Modify the resource name to be compatible with the loader.get_data
# signature - an os.path format "filename" starting with the dirname of
# the package's __file__
parts = resource.split('/')
parts.insert(0, os.path.dirname(mod.__file__))
resource_name = os.path.join(*parts)
return loader.get_data(resource_name)
你的问题的答案在这部分代码中:
mod = (sys.modules.get(package) or
importlib._bootstrap._load(spec))
它查看已经加载的包,如果我们正在寻找的包(本例中的 module.submodule
)存在,它会使用它,如果不存在,则尝试使用 [=15= 加载包].
所以让我们看看 implementation of importlib._bootstrap._load
看看发生了什么。
def _load(spec):
"""Return a new module object, loaded by the spec's loader.
The module is not added to its parent.
If a module is already in sys.modules, that existing module gets
clobbered.
"""
with _ModuleLockManager(spec.name):
return _load_unlocked(spec)
嗯,就在那里!文档说“该模块未添加到其 parent。”
表示加载了submodule
模块,但没有添加到module
模块中。因此,当我们尝试通过 module
访问 submodule
时,没有连接,因此 AtrributeError
.
get_data
方法使用这个函数是有意义的,因为它只需要包中的一些其他文件,不需要导入整个包并将其添加到它的 parent及其 parents' parent 等等。
要亲自查看,我建议使用调试器并设置一些断点。然后你就可以一步一步地看到发生了什么。