1. Importer协议
协议涉及两个对象: Finder 和 loader
1. Finder
实现了方法:
finder.find_module(fullname, path=None)
返回一个loader对象或者None。
2. Loader
实现了方法:
loader.load_module(fullname)
返回一个module对象或者raise an exception
参考:pep302
2. 注册hooks
hooks有两种,一种是Meta hooks,一种是Path hooks。
注册方式:
import importlib.machinery
import sys
# For illustrative purposes only.
SpamMetaPathFinder = importlib.machinery.PathFinder
SpamPathEntryFinder = importlib.machinery.FileFinder
loader_details = (importlib.machinery.SourceFileLoader,
importlib.machinery.SOURCE_SUFFIXES)
# Setting up a meta path finder.
# Make sure to put the finder in the proper location in the list in terms of
# priority.
sys.meta_path.append(SpamMetaPathFinder)
# Setting up a path entry finder.
# Make sure to put the path hook in the proper location in the list in terms
# of priority.
sys.path_hooks.append(SpamPathEntryFinder.path_hook(loader_details))
3. 流程
import importlib.util
import sys
def import_module(name, package=None):
"""An approximate implementation of import."""
absolute_name = importlib.util.resolve_name(name, package)
try:
return sys.modules[absolute_name] # 先查询sys.modules
except KeyError:
pass
path = None
if '.' in absolute_name:
parent_name, _, child_name = absolute_name.rpartition('.')
parent_module = import_module(parent_name)
path = parent_module.__spec__.submodule_search_locations
for finder in sys.meta_path: # 再从sys.meta_path中获取finder
spec = finder.find_spec(absolute_name, path)
if spec is not None:
break
else:
msg = f'No module named {absolute_name!r}'
raise ModuleNotFoundError(msg, name=absolute_name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[absolute_name] = module
if path is not None:
setattr(parent_module, child_name, module)
return module
其中:
sys.meta_path[-1]
类型是<class '_frozen_importlib_external.PathFinder'>
, 这个对象调用了sys.path以及sys.path_hooks。
注意:
本来按照pep302的导入逻辑,是:
for mp in sys.meta_path:
loader = mp(fullname)
if loader is not None:
<module> = loader.load_module(fullname)
for path in sys.path:
for hook in sys.path_hooks:
try:
importer = hook(path)
except ImportError:
# ImportError, so try the other path hooks
pass
else:
loader = importer.find_module(fullname)
<module> = loader.load_module(fullname)
# Not found!
raise ImportError
但后来(python3.4)引入了pep451, 接口名和调用方式发生了一些变化,使得流程如3所示。
主要参考:
pep302
pep451
importlib
New Import Hooks
Python 类库引入机制