包 __init__.py 导入所有子文件,但只从另一个脚本加载一个?

Package __init__.py import all subfiles, but only load one from another script?

我创建了一个文件结构如下的包:

- package
  - __init__.py
  - load.py
  - train.py
  - test.py

我的 __init__.py 文件只是这些文件的 classes 的导入:

from package.load import Load
from package.train import Train
from package.test import Test

大多数时候,我想加载全部三个,但有时我只想加载其中一个 class。例如,在一个临时脚本(包外)中,我希望只能像这样调用 Load class:

from package import Load

虽然上述所有方法都适用于此设计,但我有一个问题,当我像上面那样导入 Load 时,来自 train/test 的依赖项也会被加载。我如何设置 __init__.py 文件,以便我可以进行相同的 import 调用而无需从 train/test 加载依赖项?

补充说明:

我为什么这样做:我有一个问题,我希望一些人能够使用 Load class,它只使用基础 python,但是 Train/Test 文件包含专门的依赖项,只有 Load class 的用户不想使用甚至安装这些依赖项。

这是一种非常接近您想要的方式。您可以在其中定义一个函数来显式导入任何所需的函数,而不是无条件地 importing 所有包的 classes 在您的 __init__.py 中(如果 [=39 则全部导入) =] 已指定)。

__init__.py:

from pathlib import Path
import sys

print(f'In {Path(__file__).name}')

package_name = Path(__file__).parent.name
package_prefix = package_name + '.'
class_to_module_map = {'Load': 'load', 'Train': 'train', 'Test': 'test'}


def import_classes(*class_names):
    namespace = sys._getframe(1).f_globals  # Caller's globals.

    if not class_names:
        class_names = class_to_module_map.keys()  # Import them all.

    for class_name in class_names:
        module = class_to_module_map[class_name]
        temp = __import__(package_prefix+module, globals(), locals(), [class_name])
        namespace[class_name] = getattr(temp, class_name)  # Add to caller's namespace.

出于测试目的,这是我在 load.py 脚本中输入的内容:
(我还在其他两个模块中放了类似的东西,以验证它们是否得到 imported。)

load.py:

from pathlib import Path

print(f'In {Path(__file__).name}')

class Load: pass

最后是一个仅用于 import Load class:

的示例

ad_hoc.py:

from my_package import import_classes

#from my_package import Load
import_classes('Load')

test = Load()
print(test)

连同产生的输出:

In __init__.py
In load.py
<my_package.load.Load object at 0x001FE4A8>

在文件夹 oranges\ 中,这是我们的 __init__.py 文件:

__all__ = []

from pathlib import Path
from importlib import import_module
from sys import modules

package = modules[__name__]
initfile = Path(__file__)
for entry in initfile.parent.iterdir():
    is_file = entry.is_file()
    is_pyfile = entry.name.endswith('.py')
    is_not_initpy = (entry != initfile)
    if is_file and is_pyfile and is_not_initpy:
        module_name = entry.name.removesuffix('.py')
        module_path = __name__ + '.' + module_name
        module = import_module(module_path)
        setattr(package, module_name, module)
        __all__.append(module_name)

当我们执行 from oranges import * 时,oranges\__init__.py 中的代码循环遍历 oranges\ 中的 *.py 个文件(__init__.py 除外),并且对于每个 .py 文件执行以下操作:

  • 使用Python的importlib.import_module

    .py文件作为模块导入变量module
  • module 设置为 __init__.py 文件中的变量(或者更准确地说,在 oranges 包中)使用Python的setattr

  • 最后,将模块附加到 __all__ 列表