[Python] 详解Python如何实现惰性导入-lazy import

1699 0
黑夜隐士 2022-11-9 16:21:41 | 显示全部楼层 |阅读模式
目录

    前言写代码实现PEP0690 建议的做法
      其一其二



前言

如果你的 Python 程序程序有大量的 import,而且启动非常慢,那么你应该尝试懒导入,本文分享一种实现惰性导入的一种方法。虽然PEP0690已经提案让 Python 编译器(-L) 或者标准库加入这个功能,但目前的 Python 版本还未实现。
众所周知,Python 应用程序在执行用户的实际操作之前,会执行 import 操作,不同的模块可能来自不同的位置,某些模块的运行可能非常耗时,某些模块可能根本不会被用户调用,因此很多模块的导入纯粹是浪费时间。
因此我们需要惰性导入,当应用惰性导入时,运行 import foo 仅仅会把名字 foo 添加到全局的全名空间(globals())中作为一个懒引用(lazy reference),编译器遇到任何访问 foo 的代码时才会执行真正的 import 操作。类似的,from foo import bar 会把 bar 添加到命名空间,当遇到调用 bar 的代码时,就把 foo 导入。

写代码实现

那怎么写代码实现呢?其实不必写代码实现,已经有项目实现了懒导入功能,那就是 TensorFlow,它的代码并没有任何三方库依赖,我把它放到这里,以后大家需要懒导入的时候直接把LazyLoader类复制到自己的项目中去即可。
源代码如下:
#Codecopiedfromhttps://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/util/lazy_loader.py
"""ALazyLoaderclass."""

from__future__importabsolute_import
from__future__importdivision
from__future__importprint_function

importimportlib
importtypes

classLazyLoader(types.ModuleType):
"""Lazilyimportamodule,mainlytoavoidpullinginlargedependencies.

`contrib`,and`ffmpeg`areexamplesofmodulesthatarelargeandnotalways
needed,andthisallowsthemtoonlybeloadedwhentheyareused.
"""

#Thelinterrorhereisincorrect.
def__init__(self,local_name,parent_module_globals,name):#pylint:disable=super-on-old-class
self._local_name=local_name
self._parent_module_globals=parent_module_globals

super(LazyLoader,self).__init__(name)

def_load(self):
#Importthetargetmoduleandinsertitintotheparent'snamespace
module=importlib.import_module(self.__name__)
self._parent_module_globals[self._local_name]=module

#Updatethisobject'sdictsothatifsomeonekeepsareferencetothe
#LazyLoader,lookupsareefficient(__getattr__isonlycalledonlookups
#thatfail).
self.__dict__.update(module.__dict__)

returnmodule

def__getattr__(self,item):
module=self._load()
returngetattr(module,item)

def__dir__(self):
module=self._load()
returndir(module)
代码说明:
类 LazyLoader 继承自 types.ModuleType,初始化函数确保惰性模块将像真正的模块一样正确添加到全局变量中,只要真正用到模块的时候,也就是执行 __getattr__ 或 __dir__ 时,才会真正的 import 实际模块,更新全局变量以指向实际模块,并且将其所有状态(__dict__)更新为实际模块的状态,以便对延迟加载的引用,加载模块不需要每次访问都经过加载过程。
代码使用:
正常情况下我们这样导入模块:
importtensorflow.contribascontrib
其对应的惰性导入版本如下:
contrib=LazyLoader('contrib',globals(),'tensorflow.contrib')

PEP0690 建议的做法

PEP0690 的提案是在编译器( C 代码)层面实现,这样性能会更好。其使用方法有两种。

其一

一种方式是执行 Python 脚本时加入 -L 参数,比如有两个文件 spam.py 内容如下:
importtime
time.sleep(10)
print("spamloaded")
egg.py 内容如下:
importspam
print("importsdone")
正常导入情况下,会等 10 秒后先打印 "spam loaded",然后打印 "imports done",当执行 python -L eggs.py 时,spam 模块永远不会导入,应用 spam 模块压根就没有用到。如果 egg.py 内容如下:
importspam
print("importsdone")
spam
当执行 python -L eggs.py 时会先打印 "imports done",10 秒之后打印 "spam loaded")。

其二

另一种方式是调用标准库 importlib 的方法:
importimportlib
importlib.set_lazy_imports(True)
如果某些模块不能懒加载,需要排除,可以这样
importimportlib
importlib.set_lazy_imports(True,excluding=["one.mod","another"])
还可以这样:
fromimportlibimporteager_imports

witheager_imports():
importfoo
importbar 到此这篇关于详解Python如何实现惰性导入-lazy import的文章就介绍到这了,更多相关Python惰性导入内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行