inspect.getmembers无法返回本地包所有成员的原因及批量获取方法咨询
问题原因解析
这个问题我之前也碰到过,核心原因其实是Python的包默认采用懒加载策略——当你执行import xyz的时候,Python只会运行包根目录下的__init__.py,并不会自动把所有子模块(比如xyz.abc、xyz.lmn)都加载到内存里。
inspect.getmembers只能检测已经被导入到当前运行时内存中的成员,所以一开始调用getmembers(xyz, ismodule)返回空列表,完全是因为那些子模块还没被加载进来。- 至于
help('xyz.abc')之后子模块就出现在结果里,是因为help函数在查询模块文档的过程中,会自动触发该子模块的导入操作,把它加载到内存中。 - 你用
from xyz import *没效果,是因为如果xyz包的__init__.py里没定义__all__变量,*只会导入__init__.py中显式定义的名称,不会自动导入所有子模块。
解决方案:自动遍历并导入所有子模块
要不用逐个导入就能获取包下的所有子模块,你得自己遍历包的目录结构,手动导入每个子模块。可以结合os和importlib模块来实现:
基础版(仅导入一级子模块)
import os import importlib from inspect import getmembers, ismodule import xyz def import_all_submodules(package): # 获取包的绝对路径 package_path = os.path.dirname(package.__file__) # 遍历包目录下的所有文件 for filename in os.listdir(package_path): # 跳过__init__.py和非Python文件 if filename == '__init__.py' or not filename.endswith('.py'): continue # 提取子模块名称(去掉.py后缀) module_name = filename[:-3] # 导入子模块 importlib.import_module(f'{package.__name__}.{module_name}') # 先导入所有子模块 import_all_submodules(xyz) # 现在就能获取所有已加载的子模块了 print(getmembers(xyz, ismodule))
递归版(支持嵌套子目录)
如果你的包还有多层嵌套的子目录(比如xyz/subdir/def.py),可以把函数改成递归遍历的版本:
import os import importlib from inspect import getmembers, ismodule import xyz def import_all_submodules(package): package_path = os.path.dirname(package.__file__) # 递归遍历包下的所有目录和文件 for root, dirs, files in os.walk(package_path): # 计算当前目录相对于包根的路径,转换成模块层级名称 relative_path = os.path.relpath(root, package_path) if relative_path == '.': module_prefix = package.__name__ else: # 把系统路径分隔符换成模块的点分隔符 module_prefix = f"{package.__name__}.{relative_path.replace(os.sep, '.')}" for filename in files: if filename == '__init__.py' or not filename.endswith('.py'): continue module_name = filename[:-3] full_module_name = f"{module_prefix}.{module_name}" importlib.import_module(full_module_name) import_all_submodules(xyz) print(getmembers(xyz, ismodule))
简洁版(如果包定义了__all__)
如果你的包维护了__all__列表(在xyz/__init__.py里写__all__ = ['abc', 'lmn', ...]),那可以直接遍历__all__来触发导入:
from inspect import getmembers, ismodule import xyz # 遍历__all__里的模块名,触发导入 for module_name in xyz.__all__: getattr(xyz, module_name) print(getmembers(xyz, ismodule))
内容的提问来源于stack exchange,提问作者Ani Menon
相关产品推荐
相关产品推荐

