列出属于 python 包的所有模块?

发布于 2024-08-10 21:23:40 字数 206 浏览 3 评论 0原文

有没有一种简单的方法来查找 python 包中的所有模块?我发现这个旧讨论,其中并不是真正决定性的,但在我推出基于 os.listdir() 的自己的解决方案之前,我希望得到一个明确的答案。

Is there a straightforward way to find all the modules that are part of a python package? I've found this old discussion, which is not really conclusive, but I'd love to have a definite answer before I roll out my own solution based on os.listdir().

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

罪歌 2024-08-17 21:23:40

是的,您想要基于 pkgutil 或类似的东西 - 这样您就可以同等对待所有包,无论它们是在 Eggs 还是 zips 中(其中 os.listdir 没有帮助)。

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)

如何导入它们呢?您可以像平常一样使用 __import__

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)
    module = __import__(modname, fromlist="dummy")
    print "Imported", module

Yes, you want something based on pkgutil or similar -- this way you can treat all packages alike regardless if they are in eggs or zips or so (where os.listdir won't help).

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)

How to import them too? You can just use __import__ as normal:

import pkgutil

# this is the package we are inspecting -- for example 'email' from stdlib
import email

package = email
prefix = package.__name__ + "."
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, prefix):
    print "Found submodule %s (is a package: %s)" % (modname, ispkg)
    module = __import__(modname, fromlist="dummy")
    print "Imported", module
暖阳 2024-08-17 21:23:40

适合这项工作的工具是 pkgutil.walk_packages。

要列出系统上的所有模块:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

请注意 walk_packages 导入所有子包,但不导入子模块。

如果您希望列出某个包的所有子模块,那么您可以使用类似这样的内容:

import pkgutil
import scipy
package=scipy
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__,
                                                      prefix=package.__name__+'.',
                                                      onerror=lambda x: None):
    print(modname)

iter_modules 仅列出一层深度的模块。
walk_packages 获取所有子模块。
例如,对于 scipy,walk_packages 返回,

scipy.stats.stats

而 iter_modules 仅返回

scipy.stats

pkgutil 上的文档 (http://docs.python. org/library/pkgutil.html)
没有列出中定义的所有有趣的函数
/usr/lib/python2.6/pkgutil.py。

也许这意味着这些功能不是“公共”界面的一部分并且可能会发生变化。

然而,至少从 Python 2.6 开始(也许更早的版本?)
pkgutil 附带了一个 walk_packages 方法,它递归地遍历所有
可用模块。

The right tool for this job is pkgutil.walk_packages.

To list all the modules on your system:

import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=None, onerror=lambda x: None):
    print(modname)

Be aware that walk_packages imports all subpackages, but not submodules.

If you wish to list all submodules of a certain package then you can use something like this:

import pkgutil
import scipy
package=scipy
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__,
                                                      prefix=package.__name__+'.',
                                                      onerror=lambda x: None):
    print(modname)

iter_modules only lists the modules which are one-level deep.
walk_packages gets all the submodules.
In the case of scipy, for example, walk_packages returns

scipy.stats.stats

while iter_modules only returns

scipy.stats

The documentation on pkgutil (http://docs.python.org/library/pkgutil.html)
does not list all the interesting functions defined in
/usr/lib/python2.6/pkgutil.py.

Perhaps this means the functions are not part of the "public" interface and are subject to change.

However, at least as of Python 2.6 (and perhaps earlier versions?)
pkgutil comes with a walk_packages method which recursively walks through all the
modules available.

任性一次 2024-08-17 21:23:40

这对我有用:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key

This works for me:

import types

for key, obj in nltk.__dict__.iteritems():
    if type(obj) is types.ModuleType: 
        print key
哥,最终变帅啦 2024-08-17 21:23:40

感谢之前的所有答案,我刚刚将它们全部合并到一个函数中,该函数可以轻松地用于检索子模块:

def list_submodules(module) -> list[str]:
    """
    Args:
        module: The module to list submodules from.
    """
    # We first respect __all__ attribute if it already defined.
    submodules = getattr(module, "__all__", None)
    if submodules:
        return submodules

    # Then, we respect module object itself to get imported submodules.
    # Warning: Initially, the module object will respect the `__init__.py`
    # file, if its not exists, the object can partially load submoudles
    # by coda, so can lead `inspect` to return incomplete submodules list.
    import inspect
    submodules = [o[0] for o in inspect.getmembers(module)
                    if inspect.ismodule(o[1])]
    if submodules:
        return submodules

    # Finally we can just scan for submodules via pkgutil.
    import pkgutil
    # pkgutill will invoke `importlib.machinery.all_suffixes()`
    # to determine whether a file is a module, so if you get any
    # submoudles that are unexpected to get, you need to check
    # this function to do the confirmation.
    # If you want to retrive a directory as a submoudle, you will
    # need to clarify this by putting a `__init__.py` file in the
    # folder, even for Python3.
    return [x.name for x in pkgutil.iter_modules(module.__path__)]

然后您可以像这样调用它:

import module
print(list_submodules(module))

path = ...
module = importlib.import_module(path)
print(list_submodules(module))

Thanks to all previous answers, I've just merged them all into one function, which can be easily used to retrieve submodules:

def list_submodules(module) -> list[str]:
    """
    Args:
        module: The module to list submodules from.
    """
    # We first respect __all__ attribute if it already defined.
    submodules = getattr(module, "__all__", None)
    if submodules:
        return submodules

    # Then, we respect module object itself to get imported submodules.
    # Warning: Initially, the module object will respect the `__init__.py`
    # file, if its not exists, the object can partially load submoudles
    # by coda, so can lead `inspect` to return incomplete submodules list.
    import inspect
    submodules = [o[0] for o in inspect.getmembers(module)
                    if inspect.ismodule(o[1])]
    if submodules:
        return submodules

    # Finally we can just scan for submodules via pkgutil.
    import pkgutil
    # pkgutill will invoke `importlib.machinery.all_suffixes()`
    # to determine whether a file is a module, so if you get any
    # submoudles that are unexpected to get, you need to check
    # this function to do the confirmation.
    # If you want to retrive a directory as a submoudle, you will
    # need to clarify this by putting a `__init__.py` file in the
    # folder, even for Python3.
    return [x.name for x in pkgutil.iter_modules(module.__path__)]

Then you can just call it like:

import module
print(list_submodules(module))

path = ...
module = importlib.import_module(path)
print(list_submodules(module))
扛刀软妹 2024-08-17 21:23:40

我正在寻找一种方法来重新加载我正在包中实时编辑的所有子模块。它是上面答案/评论的组合,所以我决定将其作为答案而不是评论发布在这里。

package=yourPackageName
import importlib
import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.', onerror=lambda x: None):
    try:
        modulesource = importlib.import_module(modname)
        reload(modulesource)
        print("reloaded: {}".format(modname))
    except Exception as e:
        print('Could not load {} {}'.format(modname, e))

I was looking for a way to reload all submodules that I'm editing live in my package. It is a combination of the answers/comments above, so I've decided to post it here as an answer rather than a comment.

package=yourPackageName
import importlib
import pkgutil
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__+'.', onerror=lambda x: None):
    try:
        modulesource = importlib.import_module(modname)
        reload(modulesource)
        print("reloaded: {}".format(modname))
    except Exception as e:
        print('Could not load {} {}'.format(modname, e))
七颜 2024-08-17 21:23:40

如果您不仅对列出模块名称感兴趣,而且还想获取对 module 对象的引用,那么这个答案适合您:

要列出模块,请使用 pkgutil.iter_modules 如果您只需要 的直接子级模块,或 pkgutil.walk_packages 如果您需要模块的所有后代。两者都返回 ModuleInfo 元组。

要导入模块,现有答案中有各种建议,其中大多数都不是很好的选择:

import pkgutil

submodules = [
    module_info.module_finder.find_spec(
        f"{my_module.__name__}.{module_info.name}"
    ).loader.load_module()
    for module_info in pkgutil.iter_modules(my_module.__path__)
]

我的首选方法是使用 importlib.import_modulepkgutil.iter_modules 结合使用:

import importlib
import pkgutil
from types import ModuleType


def get_submodules(module: ModuleType) -> list[ModuleType]:
    return [
        importlib.import_module(f"{module.__name__}.{module_info.name}")
        for module_info in pkgutil.iter_modules(module.__path__)
    ]

关于此解决方案的一些注意事项:

  • 您可以将 pkgutil.iter_modules 替换为 pkgutil.walk_packages如果需要,
  • importlib.import_module 返回路径指定的模块,而不是路径根部的模块,例如 __import__
  • f"{module.__name__} .{module_info.name}" 我们确保所有模块都通过绝对路径引用(如果之前已导入父模块,则可以使用更短的路径加载模块,但是如果您想过滤,这可能会导致问题或比较模块)

In case you are not only interested in listing module names, but you also want to get a reference to the module objects, this answer is for you:

To list modules, use either pkgutil.iter_modules if you need just the direct children of a module, or pkgutil.walk_packages if you need all descendants of a module. Both return ModuleInfo tuples.

To import modules, there are various suggestions in the existing answers, most of which are not great choices:

  • __import__ works if you import a top level module __import__('foo'), but __import__('foo.bar') will also return the foo module, not foo.bar! You can work around this restriction, but it is cumbersome.
  • MetaPathFinder.find_module: has been deprecated since Python 3.4 and was removed in 3.12
  • MetaPathFinder.find_spec replaces find_module, you can use it by accessing the ModuleInfo.module_finder attribute, but it's a bit verbose:
import pkgutil

submodules = [
    module_info.module_finder.find_spec(
        f"{my_module.__name__}.{module_info.name}"
    ).loader.load_module()
    for module_info in pkgutil.iter_modules(my_module.__path__)
]

My preferred method is to use importlib.import_module in combination with pkgutil.iter_modules:

import importlib
import pkgutil
from types import ModuleType


def get_submodules(module: ModuleType) -> list[ModuleType]:
    return [
        importlib.import_module(f"{module.__name__}.{module_info.name}")
        for module_info in pkgutil.iter_modules(module.__path__)
    ]

a few notes on this solution:

  • you can replace pkgutil.iter_modules with pkgutil.walk_packages if needed
  • importlib.import_module returns the module specified by the path, not the module at the root of the path, like __import__
  • with f"{module.__name__}.{module_info.name}" we make sure that all modules are referenced by an absolute path (modules can be loaded with shorter paths if the parent module has been imported before, but this can cause issues if you want to filter or compare modules)
落花浅忆 2024-08-17 21:23:40

这是我突然想到的一种方法:

>>> import os
>>> filter(lambda i: type(i) == type(os), [getattr(os, j) for j in dir(os)])
[<module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'errno' (built-in)>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'sys' (built-in)>]

它肯定可以得到清理和改进。

编辑:这是一个稍微好一点的版本:

>>> [m[1] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
[<module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'errno' (built-in)>, <module 'sys' (built-in)>]
>>> [m[0] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
['_copy_reg', 'UserDict', 'path', 'errno', 'sys']

注意:这还会找到不一定位于包的子目录中的模块(如果它们被拉入包的子目录中) __init__.py 文件,因此这取决于您所说的包的“一部分”的含义。

Here's one way, off the top of my head:

>>> import os
>>> filter(lambda i: type(i) == type(os), [getattr(os, j) for j in dir(os)])
[<module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'errno' (built-in)>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'sys' (built-in)>]

It could certainly be cleaned up and improved.

EDIT: Here's a slightly nicer version:

>>> [m[1] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
[<module 'copy_reg' from '/usr/lib/python2.5/copy_reg.pyc'>, <module 'UserDict' from '/usr/lib/python2.5/UserDict.pyc'>, <module 'posixpath' from '/usr/lib/python2.5/posixpath.pyc'>, <module 'errno' (built-in)>, <module 'sys' (built-in)>]
>>> [m[0] for m in filter(lambda a: type(a[1]) == type(os), os.__dict__.items())]
['_copy_reg', 'UserDict', 'path', 'errno', 'sys']

NOTE: This will also find modules that might not necessarily be located in a subdirectory of the package, if they're pulled in in its __init__.py file, so it depends on what you mean by "part of" a package.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文