如何构建支持便捷惰性注入的Python模块?
实现惰性注入的Python模块代理
要实现你需要的效果,核心是让模块中的变量(比如y)能动态反映依赖变量(比如x)的最新值,而不是模块导入时的静态快照。我们可以通过创建模块代理类,结合AST解析来延迟计算变量值,具体实现如下:
实现思路
- 编写
lazy_import函数,返回一个模块代理对象,而非直接导入原模块。 - 解析目标模块的AST语法树,提取所有顶级变量的赋值表达式,记录变量与表达式的映射关系。
- 代理对象拦截属性的获取与设置:
- 设置属性时,将值存储在自定义属性字典中。
- 获取属性时,若该属性有对应的表达式,则基于当前的属性值重新计算表达式结果;若有自定义设置的值,则直接返回。
完整代码实现
injector_impl.py(lazy_import的实现)
import ast import sys import importlib.util class LazyModule: def __init__(self, module_name): self.module_name = module_name self._custom_attrs = {} self._attr_exprs = self._parse_module_exprs(module_name) def _parse_module_exprs(self, module_name): # 定位模块文件并解析AST spec = sys.modules.get(module_name) or importlib.util.find_spec(module_name) if not spec or not spec.origin: raise ImportError(f"无法找到模块 {module_name}") with open(spec.origin, 'r', encoding='utf-8') as f: tree = ast.parse(f.read()) attr_exprs = {} for node in ast.walk(tree): # 提取顶级单个变量的赋值表达式 if isinstance(node, ast.Assign) and len(node.targets) == 1: target = node.targets[0] if isinstance(target, ast.Name): var_name = target.id expr_str = ast.unparse(node.value) attr_exprs[var_name] = expr_str return attr_exprs def __getattr__(self, name): # 优先返回自定义设置的属性 if name in self._custom_attrs: return self._custom_attrs[name] # 计算表达式对应的当前值 if name in self._attr_exprs: return eval(self._attr_exprs[name], {}, self) # fallback到真实模块的属性(如函数、类) if self.module_name in sys.modules: return getattr(sys.modules[self.module_name], name) raise AttributeError(f"模块 {self.module_name} 不存在属性 '{name}'") def __setattr__(self, name, value): # 内部属性直接设置,外部属性存入自定义字典 if name.startswith('_'): super().__setattr__(name, value) else: self._custom_attrs[name] = value def lazy_import(module_name): return LazyModule(module_name)
测试代码
my_module.py
x = 4 y = x
injector.py
from injector_impl import lazy_import m = lazy_import("my_module") m.x = 5 print(m.y) # 输出5
注意事项
- 安全性:该实现使用了
eval解析表达式,若目标模块包含恶意代码会存在风险,仅建议在可信代码场景下使用。 - 复杂场景支持:当前实现仅处理顶级单个变量的赋值,若模块中有复杂表达式(如
y = x * 2 + z)、条件赋值或嵌套结构,需要扩展AST解析逻辑。 - 模块其他成员:对于模块中的函数、类等非变量成员,代理会自动从真实模块中获取,确保原有功能不受影响。
内容的提问来源于stack exchange,提问作者James Pinkerton
相关产品推荐
相关产品推荐

