簡體   English   中英

包或子包中所有功能的“ from src import *”動態導入

[英]“from src import *” dynamic import for all functions in a package or subpackage

目標:
我希望能夠通過“直接調用”在子包中動態導入所有功能

用法:
我的項目:

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()而進行直接調用可能非常危險,是否有任何方法可以實現我的目標?

就個人而言,我更喜歡保持明確,只將包API一部分的名稱明確地導入__init__ 您的項目變化不會太快,以至於將所有內容動態導入__init__.py將節省時間。

但是,如果您想這樣做,那么這里有一些選擇。 如果需要支持3.7之前的Python版本,則可以通過在globals()字典中戳來更新包名稱空間。 列出所有.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及更高版本,則可以定義模塊級__getattr__()__dir__()函數來實現動態查找。

在包__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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM