从模块延迟导入

问题描述 投票:0回答:1

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),但我还没有找到一个允许从外部模块延迟导入类和变量的库(所以不是我正在开发的库)。

有谁知道从模块实现延迟导入的解决方案吗?

python python-3.x python-import python-importlib
1个回答
0
投票

您可以让惰性对象充当

.
(
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

© www.soinside.com 2019 - 2024. All rights reserved.