在Python中,将一个“子”模块导入到另一个模块并列出其属性相当简单,但是当您想要导入所有子模块时,就会稍微困难一些。
我正在为现有的3D应用程序构建工具库。每个工具都有自己的菜单项和子菜单。我希望工具负责创建自己的菜单,因为这些菜单中的许多内容都基于上下文和模板而更改。我希望我的基础模块能够找到所有子模块并检查它们是否有create_menu()
函数,并在找到该函数时调用它。
有什么最简单的方法可以发现所有子模块吗?
在Python中,将一个“子”模块导入到另一个模块并列出其属性相当简单,但是当您想要导入所有子模块时,就会稍微困难一些。
我正在为现有的3D应用程序构建工具库。每个工具都有自己的菜单项和子菜单。我希望工具负责创建自己的菜单,因为这些菜单中的许多内容都基于上下文和模板而更改。我希望我的基础模块能够找到所有子模块并检查它们是否有create_menu()
函数,并在找到该函数时调用它。
有什么最简单的方法可以发现所有子模块吗?
我认为做这种插件的最好方式是使用 entry_points
和用于查询它们的API。
pkgutil.walk_packages
一样导致所有模块被导入的副作用。 - Brian Cline当我还是个善良的初学者,开始用Python编程时,我为我的模块化IRC机器人编写了这个:
# Load plugins
_plugins = []
def ifName(name):
try:
return re.match('([^_.].+)\.[^.]+', a).group(1)
except:
return None
def isValidPlugin(obj):
from common.base import PluginBase
try:
if obj.__base__ == PluginBase:
return True
else:
return False
except:
return False
plugin_names = set(ifilter(lambda a: a!=None, [ifName(a) for a in os.listdir(os.path.join(os.getcwd(), 'plugins'))]))
for plugin_name in plugin_names:
try:
plugin = __import__('plugins.'+plugin_name, fromlist=['plugins'])
valid_plugins = filter(lambda a: isValidPlugin(a), [plugin.__getattribute__(a) for a in dir(plugin)])
_plugins.extend(valid_plugins)
except Exception, e:
logger.exception('Error loading plugin %s', plugin_name)
# Run plugins
_plugins = [klass() for klass in _plugins]
这不是安全的或者“正确”的方式,但可能还是有用的。它是非常老的代码,请不要抨击我。
上面遍历文件系统查找子模块的解决方案,只要将每个插件实现为基于文件系统的模块就可以了。
更加灵活的方法是在您的主模块中有一个显式的插件列表,并且每个插件(无论是通过文件、动态地创建还是类的实例)都可以显式地将自己添加到该列表中。也许可以通过 registerPlugin 函数实现。
记住:“显式优于隐式”是 Python 之禅的一部分。
pkgutil.walk_packages()
似乎是正确的方法。以下是我如何找到可用模块列表并导入其中一个的步骤:
$ mkdir foo
$ touch foo/__init__.py
$ touch foo/bar.py
$ python
Python 3.8.2 (default, Jul 16 2020, 14:00:26)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> from pkgutil import walk_packages
>>> list(walk_packages(foo.__path__))
[ModuleInfo(module_finder=FileFinder('/home/don/git/zero-play/zero_play/foo'), name='bar', ispkg=False)]
>>> from importlib import import_module
>>> bar = import_module('foo.bar')
glob
函数来匹配目录:import os
import glob
modules = glob.glob(os.path.join('/some/path/to/modules', '*.py'))
然后你可以尝试导入它们:
checked_modules
for module in modules:
try:
__import__(module, globals(), locals()) # returns module object
except ImportError:
pass
else:
checked_modules.append(module)
import foo
foo
dir(foo)
from foo import bar
bar
这段代码的作用是导入 foo 模块,输出 foo 模块所在位置以及 foo 模块中包含的变量和函数,然后从 foo 模块中导入 bar 模块并输出 bar 模块所在位置。 - Ross Patterson
pkg_resources
) - Mechanical snailpkgutil.walk_packages
- 它将返回包和模块,并使用is_pkg
布尔标志来区分它们。因此,您可以使用它来递归查找您的模块。但是,您最终将导入所有内容(甚至不需要的内容),这可能不是理想的。 - Anentropic