如何从多个模块动态访问samenamed函数?

2024-10-03 09:18:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个项目是这样组织的:

├── main.py
└── scripts
    ├── __init__.py
    ├── script1.py
    ├── script2.py
    ├── script3.py
    ├──     .
    ├──     .
    ├──     .
    └── script30.py

每个script{i}.py只包含一个函数foo。我想要实现的是main.py中这些foo的dict以及相应的模块名称:

result = {'script1': scripts.script1.foo, ..., 'scripts30': scripts.script30.foo}

我也希望实现的是

  1. 不为每个i导入script{i}而执行此操作
  2. 一个好的和干净的代码
  3. 不显式地写入这些模块的名称,特别是在main.py内部(将来scripts中的模块数量可能会增加,每次都要更新main.py会让人恼火)

这可能吗?我所有的方法都导致了纯粹的丑陋。 我也欢迎重组我的项目的建议


Tags: 模块项目函数py名称fooinitmain
2条回答

您正在描述stdlib^{}的基本用法,实际上非常类似于他们为动态发现模块而展示的文档化用例

在这里,它适用于您的示例项目:

import importlib
import pkgutil
import scripts

submods = pkgutil.walk_packages(scripts.__path__, scripts.__name__ + ".")
result = {m.name: importlib.import_module(m.name).foo for m in submods}

Suggestions of restructuring my project are also welcome.

也许,您的项目更适合使用entry-points。每个子模块都可以将其foo注册为命名组中的入口点,然后您可以使用setuptools ^{}^{}中的更高级别API对其进行迭代

包的__init__.py脚本可以执行导入包目录中的模块等操作

这意味着您可以这样做:

__int__.py

import types as _types


def import_package_functions_named(funcname):
    """ Dynamically imports all modules in this package directory and creates a
        dictionary mapping the module's name to the target function if it exists.
    """
    import traceback
    import os
    globals_, locals_ = globals(), locals()

    registry = {}
    for filename in os.listdir(__name__):
        # Process all python files in directory that don't start with an underscore
        # (this also prevents this module from importing itself).
        if filename[0] != '_' and filename.split('.')[-1] in ('py', 'pyw'):
            modulename = filename.split('.')[0]  # Filename sans extension.
            package_module = '.'.join([__name__, modulename])
            try:
                module = __import__(package_module, globals_, locals_, [modulename])
            except:
                traceback.print_exc()
                raise

            match = module.__dict__.get(funcname)
            if isinstance(match, _types.FunctionType):  # Ensure it's a function.
                registry[modulename] = match

    return registry

这将允许您在main.py脚本中获得所需的结果,因此:

import scripts

result = scripts.import_package_functions_named("foo")
print(result)

样本输出:

{'script1': <function foo at 0x027B30B8>, 'script2': <function foo at 0x027B33D0>, 'script3': <function foo at 0x027B3418>}

相关问题 更多 >