• 学习python importlib的导入机制


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

    参考:
    pep302
    importlib

    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 类库引入机制

  • 相关阅读:
    软件界面不是艺术作品
    关于c# winForm窗体最大化的设置
    表单中的重置与取消按钮
    一个汉字=2个英文字符么?我肤浅的这么认为。
    Linux在ASCII终端下显示彩色字体
    地震勘探原理名词解释
    Linux终端使用小技巧
    8个实用而有趣Bash命令提示行
    用ps改变图片分辨率,但是不改变图片大小,上一篇不大适用。
    禁用Win7自动更新后的重启提示
  • 原文地址:https://www.cnblogs.com/lyg-blog/p/10642353.html
Copyright © 2020-2023  润新知