124

有没有一种直接的方法来列出包中所有模块的名称,而不使用__all__

例如,给定这个包:

/testpkg
/testpkg/__init__.py
/testpkg/modulea.py
/testpkg/moduleb.py

我想知道是否有标准或内置的方式来做这样的事情:

>>> package_contents("testpkg")
['modulea', 'moduleb']

手动方法是遍历模块搜索路径以找到包的目录。然后可以列出该目录中的所有文件,过滤掉唯一命名的 py/pyc/pyo 文件,去除扩展名,然后返回该列表。但这对于模块导入机制已经在内部做的事情来说似乎是相当多的工作。该功能是否暴露在任何地方?

4

10 回答 10

222

使用python2.3 及更高版本,您还可以使用该pkgutil模块:

>>> import pkgutil
>>> [name for _, name, _ in pkgutil.iter_modules(['testpkg'])]
['modulea', 'moduleb']

编辑:请注意,参数 forpkgutil.iter_modules不是模块列表,而是路径列表,因此您可能想要执行以下操作:

>>> import os.path, pkgutil
>>> import testpkg
>>> pkgpath = os.path.dirname(testpkg.__file__)
>>> print([name for _, name, _ in pkgutil.iter_modules([pkgpath])])
于 2009-08-21T09:21:02.440 回答
35
import module
help(module)
于 2009-01-28T21:35:25.747 回答
25

也许这会做你想要的?

import imp
import os
MODULE_EXTENSIONS = ('.py', '.pyc', '.pyo')

def package_contents(package_name):
    file, pathname, description = imp.find_module(package_name)
    if file:
        raise ImportError('Not a package: %r', package_name)
    # Use a set because some may be both source and compiled.
    return set([os.path.splitext(module)[0]
        for module in os.listdir(pathname)
        if module.endswith(MODULE_EXTENSIONS)])
于 2009-01-28T22:14:44.090 回答
17

不知道我是否忽略了某些东西,或者答案是否已经过时但是;

正如 user815423426 所述,这仅适用于活动对象,并且列出的模块只是之前导入的模块。

使用inspect列出包中的模块似乎很容易:

>>> import inspect, testpkg
>>> inspect.getmembers(testpkg, inspect.ismodule)
['modulea', 'moduleb']
于 2013-04-05T12:18:29.550 回答
3

这是一个适用于 python 3.6 及更高版本的递归版本:

import importlib.util
from pathlib import Path
import os
MODULE_EXTENSIONS = '.py'

def package_contents(package_name):
    spec = importlib.util.find_spec(package_name)
    if spec is None:
        return set()

    pathname = Path(spec.origin).parent
    ret = set()
    with os.scandir(pathname) as entries:
        for entry in entries:
            if entry.name.startswith('__'):
                continue
            current = '.'.join((package_name, entry.name.partition('.')[0]))
            if entry.is_file():
                if entry.name.endswith(MODULE_EXTENSIONS):
                    ret.add(current)
            elif entry.is_dir():
                ret.add(current)
                ret |= package_contents(current)


    return ret
于 2017-10-13T02:52:51.060 回答
2

这应该列出模块:

help("modules")
于 2019-01-18T22:59:41.757 回答
1

基于 cdleary 的示例,这是所有子模块的递归版本列表路径:

import imp, os

def iter_submodules(package):
    file, pathname, description = imp.find_module(package)
    for dirpath, _, filenames in os.walk(pathname):
        for  filename in filenames:
            if os.path.splitext(filename)[1] == ".py":
                yield os.path.join(dirpath, filename)
于 2016-04-21T08:33:14.593 回答
1

如果你想在 python 代码之外(从命令提示符)查看关于你的包的信息,你可以使用 pydoc。

# get a full list of packages that you have installed on you machine
$ python -m pydoc modules

# get information about a specific package
$ python -m pydoc <your package>

您将获得与 pydoc 相同的结果,但在解释器内部使用帮助

>>> import <my package>
>>> help(<my package>)
于 2019-03-22T16:28:17.463 回答
0

此处的其他答案将在检查包时运行包中的代码。如果你不想要那个,你可以像这个答案一样grep文件

def _get_class_names(file_name: str) -> List[str]:
    """Get the python class name defined in a file without running code
    file_name: the name of the file to search for class definitions in
    return: all the classes defined in that python file, empty list if no matches"""
    defined_class_names = []
    # search the file for class definitions
    with open(file_name, "r") as file:
        for line in file:
            # regular expression for class defined in the file
            # searches for text that starts with "class" and ends with ( or :,
            # whichever comes first
            match = re.search("^class(.+?)(\(|:)", line) # noqa
            if match:
                # add the cleaned match to the list if there is one
                defined_class_name = match.group(1).strip()
                defined_class_names.append(defined_class_name)
    return defined_class_names
于 2022-01-12T20:40:08.477 回答
-3
def package_contents(package_name):
  package = __import__(package_name)
  return [module_name for module_name in dir(package) if not module_name.startswith("__")]
于 2009-03-25T14:58:11.463 回答