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

问题描述 投票:0回答:4

问题

我有这样的文件夹结构:

- modules
    - root
        - abc
            hello.py
            __init__.py
        - xyz
            hi.py
            __init__.py
          blah.py
          __init__.py
      foo.py
      bar.py
      __init_.py

这是字符串格式的相同内容:

"modules",
"modues/__init__.py",
"modules/foo.py",
"modules/bar.py",
"modules/root",
"modules/root/__init__.py",
"modules/root/blah,py",
"modules/root/abc",
"modules/root/abc/__init__.py",
"modules/root/abc/hello.py",
"modules/root/xyz",
"modules/root/xyz/__init__.py",
"modules/root/xyz/hi.py"

我正在尝试以 python 导入样式格式打印出所有模块。 示例输出如下:

modules.foo
modules.bar
modules.root.blah
modules.root.abc.hello
modules.root.xyz.hi

如何在 python 中轻松做到这一点(如果可能的话,无需第三方库)?

我尝试了什么

示例代码

import pkgutil

import modules

absolute_modules = []


def find_modules(module_path):
    for package in pkgutil.walk_packages(module_path):
        print(package)
        if package.ispkg:
            find_modules([package.name])
        else:
            absolute_modules.append(package.name)


if __name__ == "__main__":
    find_modules(modules.__path__)
    for module in absolute_modules:
        print(module)

但是,这段代码只会打印出“foo”和“bar”。但不是“root”,它是子包。我也无法弄清楚如何转换它以保留它的绝对导入样式。当前代码仅获取包/模块名称,而不获取实际的绝对导入。

python python-3.x python-module python-packaging
4个回答
11
投票

这使用

setuptools.find_packages
(对于包)和
pkgutil.iter_modules
作为其子模块。也支持Python2。不需要递归,这两个函数一起使用即可处理。

import sys
from setuptools import find_packages
from pkgutil import iter_modules

def find_modules(path):
    modules = set()
    for pkg in find_packages(path):
        modules.add(pkg)
        pkgpath = path + '/' + pkg.replace('.', '/')
        if sys.version_info.major == 2 or (sys.version_info.major == 3 and sys.version_info.minor < 6):
            for _, name, ispkg in iter_modules([pkgpath]):
                if not ispkg:
                    modules.add(pkg + '.' + name)
        else:
            for info in iter_modules([pkgpath]):
                if not info.ispkg:
                    modules.add(pkg + '.' + info.name)
    return modules

2
投票

所以我终于想出了如何干净利落地做到这一点,并让 pkgutil 为您处理所有边缘情况。该代码基于 python 的

help()
函数,该函数仅显示顶级模块和包。

import importlib
import pkgutil

import sys

import modules


def find_abs_modules(module):
    path_list = []
    spec_list = []
    for importer, modname, ispkg in pkgutil.walk_packages(module.__path__):
        import_path = f"{module.__name__}.{modname}"
        if ispkg:
            spec = pkgutil._get_spec(importer, modname)
            importlib._bootstrap._load(spec)
            spec_list.append(spec)
        else:
            path_list.append(import_path)
    for spec in spec_list:
        del sys.modules[spec.name]
    return path_list


if __name__ == "__main__":
    print(sys.modules)
    print(find_abs_modules(modules))
    print(sys.modules)

这甚至适用于内置包。


1
投票
import my_module

from inspect import getmembers

print(getmembers(my_module))

这将列出模块中的所有成员,包括子模块、类、函数等。然后您可以相应地过滤列表。


-1
投票

(过时的评论) 下面的代码将为您提供代码当前工作目录中的相关包模块。

import os
import re

for root,dirname,filename in os.walk(os.getcwd()):
    pth_build=""
    if os.path.isfile(root+"/__init__.py"):
        for i in filename:
            if i <> "__init__.py" and i <> "__init__.pyc":
                if i.split('.')[1] == "py":
                    slot = list(set(root.split('\\')) -set(os.getcwd().split('\\')))
                    pth_build = slot[0]
                    del slot[0]
                    for j in slot:
                        pth_build = pth_build+"."+j
                    print pth_build +"."+ i.split('.')[0]

此代码将显示:

modules.foo
modules.bar
modules.root.blah
modules.root.abc.hello
modules.root.xyz.hi

如果您在模块文件夹之外运行它。

© www.soinside.com 2019 - 2024. All rights reserved.