以静态代码分析工具可以理解的方式从 __init__.py 中的子模块延迟导入

Mat*_*GdV 5 python lazy-loading python-import

所以,让我们假设我有一个包含 20 个逻辑分离模块的库代码的 python 包,我想从每个模块中选择 1 或 2 个类作为包的公共 api。与其强迫用户直接从模块中导入这些类,我想让它们直接从包的命名空间中使用,在__init__.py.

但是我不希望每次都急切地加载所有内容,因为每当有人试图从单个类访问一个类时加载所有 20 个模块是一种巨大的浪费(其中一些包含自己昂贵的导入),所以我实现了一个__getattr__()根据https://www.python.org/dev/peps/pep-0562/ 进行模块级别,并在有人尝试导入该类时使用其中的 importlib 加载给定类的模块。

这是一个相对干净的解决方案,但让它成为噩梦的部分是这绝对杀死了 Jedi 或 PyCharm 等静态代码分析工具。自动完成和光标悬停文档字符串对我来说意义重大,因为它们极大地提高了生产力,所以我不想编写 IDE 无法理解的库代码。

我可以编写打字存根,但这会增加更多的维护负担,因为实际上我已经对所有代码进行了类型注释并带有内联文档字符串。这不是一个很好的解决方案。

有谁知道我还能怎么做?我希望有一些聪明的方法来解决这个问题,我只是没有想过。

小智 0

这是一个老问题,但如果其他人遇到这个问题,我使用 VSCode + Sublime 静态分析的解决方案如下:

import importlib
import pathlib

# this is used instead of typing.TYPE_CHECKING as it avoids needing to import
# the typing module at all
_typing = False
if _typing:
    import sub_package
del _typing

def __getattr__(name: str):
    current_file = pathlib.Path(__file__)
    current_directory = current_file.parent
    for path in current_directory.iterdir():
        if path.stem != name:
            continue

        return importlib.import_module(f"{__package__}.{name}")

    raise AttributeError(f"{__package__} has no attribute named: {name}")


__all__ = ("sub_package",)
Run Code Online (Sandbox Code Playgroud)

我认为OP缺少的是暴露__all__变量

  • 您能否解释一下“_typing”如何帮助解决缺失的类型注释?定义一个变量而不只是删除它有何作用?在评论中,您提到它取代了“typing.TYPE_CHECKING”,但在 pep-0562 中并未提及“typing.TYPE_CHECKING”。 (2认同)