运行时覆写Test类print方法:首次抛异常后续正常(不可改原类)
运行时改写Test类print方法实现首次抛异常的解决方案
你写的代码出现无限递归的原因很直白:你把test_class的print方法替换成了raise_exception函数,而copy_test其实是test_class的引用,所以当你在raise_exception里调用copy_test.print()时,本质还是在调用raise_exception自己,这不就无限循环了嘛。
正确的思路是提前保存原始的print方法,而不是保存对象引用。因为对象的方法被替换后,再调用对象的print会指向新函数,但提前存好的原方法依然指向最初的实现。
修正后的代码
class Test: def __init__(self,a,b): self.a = a self.b = b def print(self): print(f"a-> {self.a}, and b-> {self.b}") # 实例化对象 test_instance = Test(1,2) # 先保存原始的print方法,这是关键 original_print = test_instance.print test_instance.num_raise = 0 def wrapped_print(): if test_instance.num_raise == 0: test_instance.num_raise += 1 raise Exception("save_clusters method exception") else: # 直接调用保存的原始方法,不会触发递归 return original_print() # 替换实例的print方法 test_instance.print = wrapped_print try: test_instance.print() # 首次调用:抛出异常 except Exception as e: print(f"捕获异常: {e}") test_instance.print() # 第二次调用:正常执行 test_instance.print() # 后续调用:均正常执行
更优雅的闭包版本(无需给实例加额外属性)
如果不想给实例添加num_raise这种额外属性,可以用闭包封装计数逻辑:
test_instance = Test(1,2) original_print = test_instance.print def wrap_print(): call_count = 0 def inner(): nonlocal call_count if call_count == 0: call_count += 1 raise Exception("save_clusters method exception") else: return original_print() return inner test_instance.print = wrap_print() try: test_instance.print() except Exception as e: print(f"捕获异常: {e}") test_instance.print() test_instance.print()
这两个版本都能满足需求:首次调用抛异常,之后每次调用都执行原本的print逻辑,同时避开了mock、深拷贝,也不需要修改Test类的代码。
内容的提问来源于stack exchange,提问作者An old man in the sea.
相关产品推荐
相关产品推荐

