Python 中的延迟导入已经被讨论了很长时间,并且已经提出了一些建议(例如PEP609 - 延迟导入)以使其成为未来的内置(可选)功能。
我正在开发一个 CLI 包,因此启动时间非常重要,我想通过延迟加载我正在使用的一些模块来加快速度。
到目前为止我所拥有的
通过修改Python的importlib文档中的函数来实现延迟导入,我构建了以下
LazyImport
类:
import importlib.util
import sys
from types import ModuleType
class LazyImport:
def __init__(self):
pass
def __new__(
cls,
name: str,
) -> type(ModuleType):
try:
return sys.modules[name]
except KeyError:
spec = importlib.util.find_spec(name)
if spec:
loader = importlib.util.LazyLoader(spec.loader)
spec.loader = loader
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
loader.exec_module(module)
return module
else:
raise ModuleNotFoundError(f"No module named '{name}'") from None
注意:这是我能想到的将函数转换为类的最佳方法,但如果您有更好的方法,我也欢迎对此提供反馈。
这对于顶级模块导入来说效果很好:
而不是导入(例如)
xarray
as
import xarray as xr
我会跑步
xr = LazyImport('xarray')
一切都按预期工作,不同之处在于
xarray
模块已添加到 sys.modules
但尚未加载到内存中(模块脚本尚未运行)。
xr
时(例如通过调用方法/子模块或简单地按原样引用它),模块才会加载到内存中(以便模块脚本运行)。
因此,对于上面的示例,任何这些语句都会将 xarray
模块加载到内存中:
xr.DataArray([1,2,3])
print(xr)
xr
我想要什么
现在我希望能够实现相同的结果,但是当我从模块加载类、方法或变量时。
因此(例如)不要通过以下方式导入
xarray.DataArray
类:
from xarray import DataArray as Da
我想要这样的东西:
Da = LazyImport('DataArray', _from='xarray')
这样
xarray
模块就会添加到 sys.modules
但尚未加载到内存中,并且只有当我第一次引用 Da
变量时才会加载。 Da
变量将引用 DataArray
模块的 xarray
类。
我尝试过的
我尝试了一些选项,例如
xr = LazyImport('xarray')
Da = getattr(xr, 'DataArray')
或者通过修改
LazyImport
类,但每次我引用 xr
时,xarray
模块都会加载到内存中。如果不将 Da
加载到内存中,我无法创建 xarray
变量。
我还查看了一些外部库(例如lazy_loader),但我还没有找到一个允许从外部模块延迟导入类和变量的库(所以不是我正在开发的库)。
有谁知道从模块实现延迟导入的解决方案吗?
您可以让惰性对象充当
.
(getattr
) 和 ()
(call
) 操作的代理:
class Lazy:
def __init__(self, mod, name):
self.mod = mod
self.name = name
def __getattr__(self, item):
return getattr(self._target(), item)
def __call__(self, *args, **kwargs):
return self._target()(*args, **kwargs)
def _target(self):
if self.mod not in sys.modules:
__import__(self.mod)
return getattr(sys.modules[self.mod], self.name)
r = Lazy('random', 'randint')
print(r(1, 45))
C = Lazy('collections', 'Counter')
print(C([1, 2, 1]))
但这非常脆弱——如果有一天你决定懒惰地导入一个常量怎么办?或者传递“导入的”变量?您最初的方法要好得多,只需坚持
xr.DataArray
。