Лично я предпочитаю держать вещи в явном виде и просто импортировать имена, которые являются частью API пакета, в __init__
явным образом.Ваш проект не изменится так быстро, что динамический импорт всего в __init__.py
будет экономить время.
Но если вы хотите сделать это, то у вас есть несколько вариантов здесь.Если вам нужно поддерживать версии Python старше 3.7, вы можете обновить пространство имен пакета, щелкнув по globals()
dictionary .Перечислите все .py
файлы и импортируйте их, используя importlib.import_module()
(или с __import__()
, если вам нужно поддерживать версии Python до 2.7):
__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 *Функции 1024 * и __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)