從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類

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