如何在 Python 中查找所有子模块?

Posted

技术标签:

【中文标题】如何在 Python 中查找所有子模块?【英文标题】:How to find all child modules in Python? 【发布时间】:2010-10-29 01:28:55 【问题描述】:

虽然在 Python 中将“子”模块导入另一个模块并列出其属性相当简单,但当您想要导入 所有 个子模块时会变得稍微困难​​一些。

我正在为现有的 3D 应用程序构建一个工具库。每个工具都有自己的菜单项和子菜单。我希望该工具负责创建自己的菜单,因为其中许多菜单会根据上下文和模板而变化。我希望我的基础模块能够找到所有子模块并检查 create_menu() 函数并在找到时调用它。

发现所有子模块的最简单方法是什么?

【问题讨论】:

这有关系吗? (pkg_resources from the setuptools package) 使用pkgutil.walk_packages。 模块不是包 正如@Dag 所说,可以使用pkgutil.walk_packages - 它会返回带有is_pkg bool 标志的包和模块来区分它们。因此,您可以使用它来递归查找您的模块。但是你最终会导入所有的东西(甚至是你不想要的东西),这可能是不可取的 【参考方案1】:

使用dir() 和imp module

【讨论】:

imp 看起来正是我需要的模块!谢谢。 这是一个非常简洁的答案,我不明白为什么它被接受了。 dir() 的文档假定您的对象已经具有属性。您能否详细说明一下如何使用 dir() 来查找尚未导入的模块? 不:$ mkdir foo $ touch foo/__init__.py $ touch foo/bar.py $ python Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) [GCC 4.5.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import foo >>> foo <module 'foo' from 'foo/__init__.py'> >>> dir(foo) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__'] >>> from foo import bar >>> bar <module 'foo.bar' from 'foo/bar.py'> Ross,当模块不在文件系统中时,您的方法变得不可靠,例如鸡蛋。 我的方法是from setuptools/distributeonly首先你有鸡蛋的方式!说真的,阅读文档,它可以满足您的需求。【参考方案2】:

当我还是一个善良的人并且刚开始使用 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]

这不是安全或“正确”的方式,但也许我们仍然有用。这是非常旧的代码,所以请不要打败我。

【讨论】:

是的,您应该使用提到的 dfa 之类的 imp 模块。【参考方案3】:

你可以试试globbing目录:

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)

【讨论】:

【参考方案4】:

我认为做这种插件的最好方法是使用entry_points 和API for querying them。

【讨论】:

这应该是公认的答案。当您想在运行时发现模块时,通过入口点注册模块是一种更清晰、更深思熟虑和 Pythonic 的方法,并且不会产生像pkgutil.walk_packages 那样导致所有模块都被导入的副作用。 【参考方案5】:

只要您将每个插件都实现为基于文件系统的模块,上述遍历文件系统以查找子模块的解决方案就可以了。

一种更灵活的方法是在主模块中显式地列出插件列表,并让每个插件(无论是由文件创建的模块、动态创建的模块,甚至是类的实例)都显式地将自身添加到该列表中。也许通过 registerPlugin 函数。

请记住:“显式胜于隐式”是 python 禅宗的一部分。

【讨论】:

为了让这个工作你必须有办法确保在你启动你的应用程序时导入插件模块,否则他们将没有机会注册自己。这是鸡和蛋的问题。如果不遍历/自动导入模块,您最终会手动导入主模块中的所有模块,不需要注册功能,但打破了主不需要了解特定插件的原则【参考方案6】:

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')

【讨论】:

以上是关于如何在 Python 中查找所有子模块?的主要内容,如果未能解决你的问题,请参考以下文章

如何递归获取 python 包中的所有子模块?

python 如何调用子文件下的模块

如何在asyncio python中使用子进程模块限制并发进程数

在一个多模块的python项目中,如何在子模块中引用项目的根目录?

如何在python子进程模块中执行用户输入(如日期)作为命令[重复]

python Python脚本列出给定Python包的所有子模块