【问题标题】:"from src import *" dynamic import for all functions in a package or subpackage"from src import *" 动态导入包或子包中的所有函数
【发布时间】:2019-02-05 12:34:10
【问题描述】:

目标:
我希望能够通过“直接调用”动态导入子包中的所有函数

用法:
我的项目:

project/
|-- main.py
|-- src/
|---- __init__.py
|---- foo.py
|---- bar.py 

foo.py 只有一个功能:

def foo_funct(): 
    print("foo")

bar.py 只有一个功能:

def bar_funct():
    print("bar")

最后是main.py

from src import * 
(...)
foo_funct()
bar_funct()
(...)

评论:

  1. 如果我的__init__.py 是这样的

    import os 
    __all__ = [i.replace(".py", "") for i in os.listdir(os.getcwd()+"/src/") if "__" not in i]
    

    我可以打电话给foo.foo_funct()bar.bar_funct(),但不能打电话给foo_funct()bar_funct()

  2. 如果我的__init__.py 是这样的:

    from src.foo import *
    from src.bar import *
    

    我可以打电话给foo_funct()bar_funct(),但是对于每个新的子包,我都必须修改我的__init__.py

  3. 假设 from src import * 不是最 Pythonic 的方法,并假设由于可能的命名冲突(例如 a.tree_funct()b.tree_funct())直接调用可能非常危险,有什么方法可以达到我的目标了吗?

【问题讨论】:

    标签: python python-3.x import python-import importerror


    【解决方案1】:

    就个人而言,我更喜欢保持明确,并且只是将作为包 API 一部分的名称显式导入到 __init__ 中。您的项目不会发生如此迅速的变化,以至于将所有内容动态导入__init__.py 会节省时间。

    但是,如果您想这样做,那么您有几个选择。如果您需要支持早于 3.7 的 Python 版本,则可以通过戳globals() dictionary 来更新包命名空间。列出所有.py 文件并使用importlib.import_module() 导入它们(如果需要支持2.7 之前的Python 版本,则使用__import__()):

    __all__ = []
    
    def _load_all_submodules():
        from pathlib import Path
        from importlib import import_module
    
        g = globals()
        package_path = Path(__file__).resolve().parent
        for pyfile in package_path.glob('*.py'):
            module_name = pyfile.stem
            if module_name == '__init__':
                continue
            module = import_module(f'.{module_name}', __package__)
            names = getattr(
                module, '__all__', 
                (n for n in dir(module) if n[:1] != '_'))
            for name in names:
                g[name] = getattr(module, name)
                __all__.append(name)
    
    _load_all_submodules()
    del _load_all_submodules
    

    以上内容保持命名空间干净; _load_all_submodules() 函数运行后,它会从包中删除。它使用__file__ 全局来确定当前路径并从那里找到任何同级.py 文件。

    如果只需要支持 Python 3.7 及以上版本,可以定义module-level __getattr__() and __dir__() functions 实现动态查找。

    在你的包 __init__.py 文件中使用这些钩子可能看起来像:

    def _find_submodules():
        from pathlib import Path
        from importlib import import_module
    
        package_path = Path(__file__).resolve().parent
        return tuple(p.stem for p in package_path.glob('*.py') if p.stem != '__init__')
    
    __submodules__ = _find_submodules()
    del _find_submodules
    
    
    def __dir__():
        from importlib import import_module
        names = []
        for module_name in __submodules__:
            module = import_module(f'.{module_name}', __package__)
            try:
                names += module.__all__
            except AttributeError:
                names += (n for n in dir(module) if n[:1] != '_')
        return sorted(names)
    
    
    __all__ = __dir__()
    
    
    def __getattr__(name):
        from importlib import import_module
        for module_name in __submodules__:
            module = import_module(f'.{module_name}', __package__)
            try:
                # cache the attribute so future imports don't call __getattr__ again
                obj = getattr(module, name)
                globals()[name] = obj
                return obj
            except AttributeError:
                pass
        raise AttributeError(name)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 2018-12-09
      • 2021-05-09
      • 2019-10-27
      • 1970-01-01
      • 1970-01-01
      • 2016-06-29
      相关资源
      最近更新 更多