
本文介绍一种基于 sys.modules 和运行时全局命名空间分析的方法,准确识别模块中所有非本地定义的导入对象及其来源模块,适用于依赖层级校验等工程化场景。
本文介绍一种基于 `sys.modules` 和运行时全局命名空间分析的方法,准确识别模块中所有非本地定义的导入对象及其来源模块,适用于依赖层级校验等工程化场景。
在 Python 工程中,常需验证模块依赖方向是否符合架构约定(例如:底层模块不应反向导入高层模块)。但仅通过 globals() 或 inspect.getmembers() 获取模块属性时,无法天然区分该名称是本模块定义的,还是从其他模块导入的——尤其当导入的是简单值(如整数、字符串、列表)时,inspect.getmodule(obj) 会失效(因其不携带模块归属信息)。
核心思路是:利用 Python 的模块缓存机制 sys.modules 构建“符号 → 定义模块”的逆向映射。所有已加载模块(包括被导入的 a)都注册在 sys.modules 中;其 __dict__(即 vars(module))记录了该模块定义的所有顶层名称。我们遍历所有已加载模块的变量,收集所有非双下划线(__*__)的公共名称,并建立 {name: defining_module} 映射。随后,对比当前模块的 globals(),筛选出那些既存在于映射中、又不是模块类型对象本身的项——这些正是被导入的非模块对象(如 CONSTANT),并可直接获取其源模块名。
以下为完整可运行示例(适配 b.py 场景):
import sys
from a import CONSTANT # 假设 a.py 已存在且含 CONSTANT = 2
CONSTANT2 = 4 # 本地定义,应被过滤
# 构建:所有已加载模块中定义的非 dunder 名称 → 定义模块
ModuleType = type(sys)
imported_from = {}
for module in sys.modules.values():
if module is None:
continue
for name in vars(module):
if not (name.startswith('__') and name.endswith('__')):
# 避免覆盖:后加载模块同名变量会覆盖先加载的,但通常合理
imported_from[name] = module
# 筛选当前模块 globals() 中属于导入、且非模块类型的项
for name, obj in globals().items():
if name in imported_from and not isinstance(obj, ModuleType):
source_module = imported_from[name].__name__
print(f"'{name}' imported from module '{source_module}'")输出:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
立即学习“Python免费学习笔记(深入)”;
'CONSTANT' imported from module 'a'
⚠️ 注意事项:
- 此方法依赖 sys.modules 的完整性,要求目标模块已被成功导入(惰性导入需提前触发);
- 若存在同名冲突(如多个模块定义同名变量),映射以 sys.modules 遍历顺序为准,建议结合 importlib.util.find_spec() 进一步校验优先级;
- 不适用于动态生成的名称(如 exec()、setattr() 创建的变量),因其未登记在源模块 __dict__ 中;
- 生产环境建议封装为工具函数,并加入缓存与异常处理,避免重复遍历 sys.modules。
该方案绕过了对象本身的元信息缺失问题,转而从 Python 解释器的模块管理机制出发,稳定、轻量且无需修改源模块,是进行模块依赖审计的可靠基础。

















