我有一个这样构建的项目:
├── 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}
我也希望实现
- 在不为每个
i
导入script{i}
的情况下执行此操作 - 一个干净漂亮的代码
- 没有显式写入这些模块的名称,尤其是在
main.py
中(scripts
中的模块数量将来可能会增长,每次更新main.py
都会很烦人(
这可能吗?我所有的做法都导致了纯粹的丑陋。也欢迎对我的项目进行重组的建议。
包的__init__.py
脚本可以执行一些操作,例如导入包目录中的模块。
这意味着你可以做这样的事情:
script{i}.py
0:
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_points
或entrypoints
中的更高级别API对其进行迭代。