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 類庫引入機制