从tensorflow的LazyLoader谈python的importlib

从LayzerLoader 讲起

python 的module 可以通过三种方式进行importing,分别是import, importlib.import_module(), __import__。

import 语句结合了两个操作:首先搜索指定的模块,然后将搜索结果绑定到当前的作用域。

import() 直接调用,将会执行模块的搜索以及在找到时进行模块的创建。
例如:

 module2 = __import__ ("data.api")

 print(module2.__dict__)
 """
    {'__name__': 'data', '__doc__': None, '__package__': 'data', '__loader__': <_frozen_importlib_external._NamespaceLoader object at 0x10c4c3588>, '__spec__': ModuleSpec(name='data', loader=<_frozen_importlib_external._NamespaceLoader object at 0x10c4c3588>, submodule_search_locations=_NamespacePath(['/Users/wuyongyu/PycharmProjects/untitled1/data', '/Users/wuyongyu/PycharmProjects/untitled1/data'])), '__file__': None, '__path__': _NamespacePath(['/Users/wuyongyu/PycharmProjects/untitled1/data', '/Users/wuyongyu/PycharmProjects/untitled1/data']), 'api': <module 'data.api' from '/Users/wuyongyu/PycharmProjects/untitled1/data/api.py'>}

 """
# 将会报错:TypeError: 'module' object is not subscriptable
 api_instance = module2["api"].classA()

# 执行正确
api_instance = module2.api.classA()

importlib 模块提供了一个丰富的API来与导入系统进行交互。importlib.import_module() 提供了比内置的__import__()更推荐的、更简单的API用来发起调用导入机制。

tensorflow中定义了LazyerLoader

通过importlib 导入module,以及获取module的__dict__属性包含的内容。
在真正使用module的方法或者类的时候,可以通过__getattr__获取加载相对应的方法或者类。

import unittest
import types
import importlib
import sys

class testModules(unittest.TestCase):
    # 1 使用 types.ModuleType
    def test_Test_subclass(self):
        # 之类通过 ModuleType 定义一个module
        m = types.ModuleType("m")
        class Hello(object):
            def hello(self):
                pass
        def function1():
            print("this is function 1")
        # 向module 内部增加__dict__ 属性,包括class 和 fucntion
        m.test_class = Hello
        m.test_function = function1

        # 可以通过module getattr 进行调用
        a = m.test_class()
        a.hello()
        m.test_function()

    # ModuleType 定义的module 不能被直接 import
    # 可以将module 增加到 sys.module 进行调用
    def test_Test_import_ModuleType(self):
        m = types.ModuleType("m")

        class Hello(object):
            def hello(self):
                pass
        def function1():
            print("this is function 1")
        m.test_class = Hello
        m.test_function = function1
        sys.modules["m"] = m
        import m as mm
        mm.test_function()


# tensorflow LazyLoader
class LazyLoader(types.ModuleType):
    """ Layz 加载Module ,可以让module进行转发"""
    def __init__(self, local_name, parent_module_globals, name):
        self._local_name = local_name
        self._parent_module_globals = parent_module_globals
        super(LazyLoader, self).__init__(name)

    def _load(self):
        """导入module, 并且放到 parent_module里面"""
        print("LazyLoader->_load")
        module = importlib.import_module(self.__name__)
        self._parent_module_globals[self._local_name] = module
        # 将module的属性更新到LazyLoader
        self.__dict__.update(module.__dict__)
        return module

    # 返回import_module 的module属性
    def __getattr__(self, item):
        print("LazyLoader->__getatrr__ item:", item)
        module = self._load()
        # print("type(module):", type(module))
        return getattr(module, item)

    def __dir__(self):
        module = self._load()
        return dir(module)

    def __reduce__(self):
        return __import__, (self.__name__,)

    def test(self):
        print("this is test attr")

if __name__ == '__main__':
    # unittest.main()
    _module = LazyLoader("m", globals(), "data.api") # 将 api和_module 放入 globals()
    sys.modules.setdefault("hh", _module)       # 这里只是设置 hh 指向 _module

    # 尝试获取 LazyLoader的属性,如果没有,则通过__getattr__获取 module的属性
    getattr(globals()["_module"], "test")()
    w = getattr(globals()["_module"], "__name__")

    import hh      # 因为 sys.module.setdefault 所以可以 import,这里并没有实际执行_load
    hh.api()       # 通过 __getattr__ 执行 _load操作加载 api类

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章