如何动态生成嵌套with语句?基于列表实现patch.object批量嵌套
如何用列表生成嵌套with语句(模拟多层patch.object)
针对你需要把列表中的模块-类对转换成嵌套with patch.object的需求,这里有两种实用的实现方式,效果和手动写多层嵌套完全一致:
方法一:用ExitStack管理多个上下文(推荐)
Python的contextlib.ExitStack专门用来动态管理多个上下文管理器,它会按顺序进入每个上下文,退出时逆序清理,和手动嵌套的逻辑完全匹配。
示例代码
from contextlib import ExitStack from unittest.mock import patch import importlib # 你的模块-类列表 patch_targets = [("module1", "cls1"), ("module2", "cls2"), ("module3", "cls3")] with ExitStack() as stack: # 遍历列表,逐个注册patch上下文 for module_name, cls_name in patch_targets: # 动态导入模块(如果模块没提前导入的话) module = importlib.import_module(module_name) # 获取目标类 target_cls = getattr(module, cls_name) # 将patch加入ExitStack stack.enter_context(patch.object(target_cls, "foo")) # 这里写原本放在嵌套with里的业务逻辑 print("所有patch已生效,执行操作...") # do something
说明
- 不需要手动写多层嵌套,列表多长就自动处理多少层
- 异常情况下也能保证所有上下文按逆序正确退出,和手动嵌套的行为一致
- 如果模块已经提前导入,也可以直接用
globals()[module_name]代替importlib.import_module
方法二:用reduce手动嵌套(不推荐,可读性差)
如果不想用ExitStack,也可以用functools.reduce来手动嵌套上下文管理器,但这种方式可读性不如ExitStack,且需要注意异常处理:
示例代码
from functools import reduce from unittest.mock import patch import importlib patch_targets = [("module1", "cls1"), ("module2", "cls2"), ("module3", "cls3")] # 先创建所有patch上下文对象 patch_contexts = [] for module_name, cls_name in patch_targets: module = importlib.import_module(module_name) target_cls = getattr(module, cls_name) patch_contexts.append(patch.object(target_cls, "foo")) # 定义要执行的业务函数 def do_something(): print("执行操作...") # 你的业务逻辑 # 用reduce实现嵌套上下文 def run_with_nested_contexts(contexts, func): def wrap(ctx): with ctx: return func() # 从最后一个上下文开始嵌套,模拟手动从内到外的写法 return reduce(lambda f, ctx: lambda: wrap(ctx) or f(), reversed(contexts), lambda: func())() # 执行 run_with_nested_contexts(patch_contexts, do_something)
说明
- 这种方式是通过函数嵌套模拟with语句的嵌套,逻辑和手动写多层with一致,但代码更绕,维护成本高
- 除非有特殊场景,否则优先用ExitStack
内容的提问来源于stack exchange,提问作者Dejan
相关产品推荐
相关产品推荐

