繁体   English   中英

如何在 python 中懒惰地导入模块?

[英]How can I lazily import a module in python?

我有需要依赖项才能被实例化的类,但在其他方面是可选的。 我想懒惰地导入依赖项,如果 class 不可用,则无法实例化它们。 请注意,package 级别不需要这些依赖项(否则它们将通过 setuptools 强制执行)。 我目前有这样的事情:

class Foo:
    def __init__(self):
        try:
            import module
        except ImportError:
            raise ModuleNotFoundError("...")

    def foo(self):
        import module

因为这种 try/except 模式很常见,所以我想将它抽象为一个惰性导入器。 理想情况下,如果module可用,我不需要在Foo.foo中再次导入它,所以我希望module__init__中导入后可用。 我尝试了以下方法,如果numpy不可用,它会填充globals()并且无法实例化 class ,但它会污染全局命名空间。

def lazy_import(name, as_=None):
    # Doesn't handle error_msg well yet
    import importlib
    mod = importlib.import_module(name)
    if as_ is not None:
        name = as_
    # yuck...
    globals()[name] = mod

class NeedsNumpyFoo:
    def __init__(self):
        lazy_import("numpy", as_="np")

    def foo(self):
        return np.array([1,2,])

如果导入没有失败,我可以在 class 之外实例化模块并指向导入的模块,但这与globals()方法相同。 或者lazy_import可以返回mod ,我可以在需要模块时调用它,但这无异于像以前一样在任何地方导入它。

有没有更好的方法来处理这个?

Pandas actually has a function import_optional_dependency which may make a good example ( link GitHub ) as used in SQLAlchemyEngine ( link GitHub )

但是,这仅在 class __init__期间使用以获得有意义的错误(默认为raise ImportError(...) )或警告缺少或旧的依赖项(这可能是更实际的使用它,因为较旧或较新的依赖项可能如果它们存在,则可以在任何地方正确导入,但不能正常工作或被明确测试甚至是意外的本地导入)

我会考虑做类似的事情,要么不费心进行特殊处理,要么只在__init__中进行(然后可能仅适用于您对版本感兴趣的少数情况等),否则只需在需要的地方导入

class Foo():
    def __init__(self, ...):
        import bar  # only tests for existence

    def usebar(self, value):
        import bar
        bar.baz(value)

有可能您可以分配给 class 的属性,但这可能会导致一些麻烦或混乱(因为一旦导入,导入应该已经在globals中可用)

class Foo():
    def __init__(self, ...):
        import bar
        self.bar = bar

    def usebar(self, value):
        self.bar.baz(value)

用包装器对其进行快速测试,似乎工作正常:

def requires_math(fn):
    def wrapper(*args, **kwargs):
        global math
        try:
            math
        except NameError:
            import math
        return fn(*args, **kwargs)
    return wrapper

@requires_math
def func():
    return math.ceil(5.5)

print(func())

编辑:更高级的一个,适用于任何模块,并确保它是一个模块,以防它被设置为其他东西。

from types import ModuleType

def requires_import(*mods):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            for mod in mods:
                if mod not in globals() or not isinstance(globals()[mod], ModuleType):
                    globals()[mod] = __import__(mod)
            return fn(*args, **kwargs)
        return wrapper
    return decorator

@requires_import('math', 'random')
def func():
    return math.ceil(random.uniform(0, 10))

print(func())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM