學習python importlib的導入機制

1. Importer協議

協議涉及兩個對象: Finder 和 loaderhtml

1. Finder

實現了方法:
finder.find_module(fullname, path=None)python

返回一個loader對象或者None。git

2. Loader

實現了方法: loader.load_module(fullname)github

返回一個module對象或者raise an exceptionapp

參考:pep302ide

2. 註冊hooks

hooks有兩種,一種是Meta hooks,一種是Path hooks。 註冊方式:spa

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))

參考:
pep302 importlibcode

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。htm

注意: 原本按照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 類庫引入機制

相關文章
相關標籤/搜索