如何实现自动调用父类方法的Python装饰器?
我有一个包含通用方法的类层次结构,希望子类在执行自身方法前自动调用父类的对应方法,但根类显然没有父方法可调用。
手动在子类方法中添加super().f(...)并非难事,但有时会忘记某个类并非根类而遗漏该调用,直到追踪调用链时才发现中断问题。想知道是否存在符合需求的现成装饰器,或是优化自己实现的更好方式。
第一次尝试的实现
def extend_super(orig_method): def decorated(*args, **kwargs): if len(args) >= 1: selfarg = args[0] selfarg_qualname = selfarg.__class__.__qualname__ expect_qualname = f"{selfarg_qualname}.{orig_method.__name__}" if expect_qualname == orig_method.__qualname__: try: super_method = getattr(super(type(selfarg), selfarg), orig_method.__name__) super_method.__func__(*args, **kwargs) except AttributeError: pass return orig_method(*args, **kwargs) return decorated
该实现可以正常工作:会调用父类方法,在根类处停止,甚至能兼容错误装饰普通函数。但它依赖字符串操作,显得较为笨拙。
编辑1:优化性能——缓存父类方法
原本希望将查找父类方法的开销移出函数调用过程,尝试通过gc获取装饰器所传入函数的所有引用列表,但列表中并无类的引用——因为装饰器总是在类创建前被调用,此时尚不存在可查找的类。
参考了统计函数调用次数的装饰器思路(通过在被装饰函数上添加字段存储数据),设想装饰器可在首次调用时查找父类方法并保存,后续调用直接使用已保存的方法以提升效率,实现代码如下:
def extend_super(orig_method): def decorated(*args, **kwargs): if not decorated.checked_super_method: decorated.checked_super_method = True if len(args) >= 1: selfarg = args[0] selfarg_qualname = selfarg.__class__.__qualname__ expect_qualname = f"{selfarg_qualname}.{orig_method.__name__}" if expect_qualname == orig_method.__qualname__: super_class = super(type(selfarg), selfarg) try: decorated.super_method = getattr(super_class, orig_method.__name__) except AttributeError: pass if decorated.super_method is not None: decorated.super_method.__func__(*args, **kwargs) return orig_method(*args, **kwargs) decorated.checked_super_method = False decorated.super_method = None return decorated
Python支持为函数添加属性,缓存操作结果就是其典型应用场景,因此我对这个实现较为满意。
编辑2:发现实现的致命缺陷
上述实现仅在最底层子类中有效,因为self参数始终为首次调用的类实例。super()会找到上一级父类方法,但调用该方法时仍使用原始self参数,导致super()每次都找到同一层级,最终引发递归调用直至达到递归深度限制。
真正的解决方案或许需要手动遍历selfarg.__class__.__bases__列表,直至找到当前函数所属的类,或遍历至无父类(s.__class__.__bases__[0] == type),但这需要大量额外工作,而我原本只是想简化一个小问题,因此不愿投入过多精力。
内容的提问来源于stack exchange,提问作者John Bayko

