参考链接:
An approach to lazy importing in Python 3.7(这个是参考源)
Python3.7中一种懒加载的方式(中文翻译)
原博客核心:
以前的两种惰性/延迟加载方法:
①本地子功能区加载而非程序启动时的全局加载。直到你的程序运行需要这个库的时候才进行加载;缺点:易重复载入库文件、容易遗忘库载入的范围。
②惰性加载。需要模块的时候触发 ModuleNotFoundError 提前发现这个模块,而延迟的只是后续补加载过程;缺点:显式优于隐式、如果一个模块希望立即加载,那么在延迟加载时,它可能会严重崩溃。(Mercurial实际上开发了一个模块黑名单,以避免延迟加载来解决这个问题,但是他们必须确保对其进行更新,因此这也不是一个完美的解决方案。)
博主提出来的最新的方法:
在Python 3.7中,模块现在可以在其上定义__getattr__(),允许编写一个函数,在模块上的属性不可用时导入模块。这样做的缺点是使它成为一个惰性导入而不是一个加载,因此很晚才发现是否会引发ModuleNotFoundError。但是它是显式的,并且仍然是为您的模块全局定义的,因此更容易控制。
改进方向:发现导入错误被推迟,如何提前获知这个可能出现的导入错误防止程序抛出异常并终止。
代码段1:demo1.py
1 import importlib 2 3 # 这个是实现lazy_import的功能函数 4 def lazy_import(importer_name, to_import): 5 module = importlib.import_module(importer_name) # 直接加载调用的后一级函数 6 7 import_mapping = {} # 字典 键名:有可能为缩写名 值名:为原始可查找库名,例如:import_mapping['np'] = 'numpy' 8 for name in to_import: 9 importing, _, binding = name.partition(' as ') 10 if not binding: 11 _, _, binding = importing.rpartition('.') 12 import_mapping[binding] = importing 13 14 def __getattr__(name): 15 if name not in import_mapping: # 如果这个库没在import_mapping中,就抛出异常错误,并且中断 16 message = f'module {importer_name!r} has no attribute {name!r}' 17 raise AttributeError(message) 18 importing = import_mapping[name] 19 imported = importlib.import_module(importing,module.__spec__.parent) 20 # print('name=',name,'module=',module,'module.__spec__=',module.__spec__,'module.__spec__.parent=',module.__spec__.parent) 21 setattr(module, name, imported) # sub, np, numpy 22 return imported 23 24 return module, __getattr__ #返回一个库和一个方法
代码段2:sub.py
lazy_import 在python3.7中已经可以直接使用了
1 # In pkg/__init__.py with a pkg/sub.py. 2 import demo1 3 4 # print('sub.py中的__name__ =', __name__) #当其他程序调用这个程序的时候 __name__ = 'sub',自己为主程序的时候为 '__main__',这里我第一次使用的时候就出错了,直接在这个程序中测试 5 mod, __getattr__ = demo1.lazy_import(__name__, {'sys', '.sub as thingy', 'numpy as np'}) 6 7 8 def test1(): 9 print('sys运行正常') 10 return mod.sys 11 12 13 def test2(): 14 return mod.thingy.answer 15 16 17 def test3(): 18 print('numpy运行正常') 19 return mod.np
代码段3:mid_test.py
1 import sub 2 3 ### 异常检测,str_out是不存在的,抛出异常处理 4 # module1 = sub.str_out() 5 6 module2 = sub.test3() 7 print(module2) 8 # <module 'numpy' from 'C:\ProgramData\Anaconda3\envs\lib\site-packages\numpy\__init__.py'> 9 10 print(module2.array([1, 2, 3, 4])) 11 # [1 2 3 4] 12 13 print(module2.__spec__) 14 # ModuleSpec( 15 # name='numpy', 16 # loader=<_frozen_importlib_external.SourceFileLoader object at 0x000001E4879A6F98>, 17 # origin='C:\ProgramData\Anaconda3\envs\lib\site-packages\numpy\__init__.py', 18 # submodule_search_locations=['C:\ProgramData\Anaconda3\envs\lib\site-packages\numpy']) 19 20 print(module2.__spec__.parent) 21 # numpy