如何解决抽象类重写时的最大递归深度超限问题?
解决MethodInterceptor中的递归深度超限问题
问题场景
尝试实现一个通过__getattribute__拦截方法调用,在方法执行前后触发before_execute和after_execute的抽象类,相关代码及运行错误如下:
父类定义
from abc import abstractmethod, ABC class MethodInterceptor(object): @abstractmethod def before_execute(self): pass @abstractmethod def after_execute(self): pass def __getattribute__(self, name): attr = object.__getattribute__(self, name) if hasattr(attr, '__call__'): def new_func(*args, **kwargs): self.before_execute() result = attr(*args, **kwargs) self.after_execute() return result return new_func else: return attr
子类与业务类
class LoggerInterceptor(MethodInterceptor): def before_execute(self): print("before") def after_execute(self): print("after") class Worker(LoggerInterceptor): def __init__(self): self.name = "worker" def work(self): print(f"hi I am {self.name}") c = Worker() c.work()
运行错误
Traceback (most recent call last): File "/Users/xxx/Projects/Pycharm/method_interceptor.py", line 47, in <module> c.work() File "/Users/xxx/Projects/Pycharm/method_interceptor.py", line 18, in new_func self.before_execute() File "/Users/xxx/Projects/Pycharm/method_interceptor.py", line 18, in new_func self.before_execute() File "/Users/xxx/Projects/Pycharm/method_interceptor.py", line 18, in new_func self.before_execute() [Previous line repeated 996 more times] RecursionError: maximum recursion depth exceeded
错误原因
__getattribute__会拦截所有属性和方法的访问请求,包括before_execute和after_execute。当包装后的work方法调用self.before_execute()时,又会触发__getattribute__,返回被包装后的before_execute方法;而这个包装后的方法内部又会再次调用self.before_execute(),形成无限递归,最终导致栈溢出。
解决方案
以下两种方式均可解决递归问题:
方式1:排除拦截钩子方法
修改__getattribute__,不对before_execute和after_execute进行包装,直接返回原始方法:
from abc import abstractmethod, ABC class MethodInterceptor(object): @abstractmethod def before_execute(self): pass @abstractmethod def after_execute(self): pass def __getattribute__(self, name): attr = object.__getattribute__(self, name) # 仅对非钩子方法进行包装 if hasattr(attr, '__call__') and name not in ('before_execute', 'after_execute'): def new_func(*args, **kwargs): self.before_execute() result = attr(*args, **kwargs) self.after_execute() return result return new_func else: return attr
方式2:直接获取原始钩子方法
在包装函数内部,通过object.__getattribute__直接获取未被包装的原始钩子方法,避免触发__getattribute__拦截:
from abc import abstractmethod, ABC class MethodInterceptor(object): @abstractmethod def before_execute(self): pass @abstractmethod def after_execute(self): pass def __getattribute__(self, name): attr = object.__getattribute__(self, name) if hasattr(attr, '__call__'): def new_func(*args, **kwargs): # 直接获取原始方法,跳过拦截逻辑 before = object.__getattribute__(self, 'before_execute') before() result = attr(*args, **kwargs) after = object.__getattribute__(self, 'after_execute') after() return result return new_func else: return attr
运行结果
修改后执行c.work(),会正常输出:
before hi I am worker after
内容的提问来源于stack exchange,提问作者barteloma
相关产品推荐
相关产品推荐

