【问题标题】:Package __init__.py import all subfiles, but only load one from another script?包 __init__.py 导入所有子文件,但只从另一个脚本加载一个?
【发布时间】:2023-03-26 14:00:01
【问题描述】:

我创建了一个具有以下文件结构的包:

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

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

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

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

from package import Load

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

补充说明:

我这样做的原因:我有一个问题,我希望某些人能够使用 Load 类,它只使用基本 python,但是训练/测试文件包含专门的依赖项,用户只使用 Load类不想使用甚至安装。

【问题讨论】:

  • “但是,训练/测试文件包含专门的依赖项,只有 Load 类的用户不想使用甚至安装这些依赖项。”那么package.load应该是单独打包了吧?
  • 一般来说,您可能是正确的。在这种情况下,包是一个通用包,单个团队将广泛用于各种项目,并且希望将包保留在单个导入调用中(主要是利用环境变量)。问题是用户拥有广泛的技术知识,并且一些更复杂的依赖项比尝试安装它们更麻烦。
  • 这个问题需要更多答案!!更少的输入 => 更简单的代码

标签: python python-3.x package


【解决方案1】:

这是一种非常接近你想要做的事情的方法。您可以在其中定义一个函数来显式导入所需的任何类(或者如果未指定,则全部导入),而不是无条件地 import 将所有包的类放在您的 __init__.py 中。

__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 类的示例:

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>

【讨论】:

    【解决方案2】:

    在文件夹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

    • 使用 Python 的setattrmodule 设置为__init__.py 文件中的变量(或者更准确地说,在oranges 包中)

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-23
      • 1970-01-01
      • 1970-01-01
      • 2020-10-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多