如何从多个模块动态访问相同名称的函数



我有一个这样构建的项目:

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

每个CCD_ 1恰好由一个函数CCD_。我想要实现的是这些foo的dict以及main.py:中相应的模块名称

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

我也希望实现

  1. 在不为每个i导入script{i}的情况下执行此操作
  2. 一个干净漂亮的代码
  3. 没有显式写入这些模块的名称,尤其是在main.py中(scripts中的模块数量将来可能会增长,每次更新main.py都会很烦人(

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

包的__init__.py脚本可以执行一些操作,例如导入包目录中的模块。

这意味着你可以做这样的事情:

script{i}.py0:

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>}

您正在描述stdlibpkgutil的基本用法,实际上与他们展示的动态发现模块的文档用例非常相似。

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

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}

也欢迎对我的项目进行重组的建议。

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

最新更新