如何在Python抽象元类的方法中要求子类实现时必须显式调用super()
解答
1. 是否可以仅要求部分抽象方法重写时必须显式调用super()?
可以实现仅对部分抽象方法做强制要求,不需要对所有抽象方法生效,只需要给需要添加强制规则的方法单独做标记,再配合自定义逻辑校验即可。
2. 具体实现方案
常用的可靠实现是运行时检测方案,准确率更高,不会出现静态语法检测的漏判/误判问题,实现逻辑如下:
实现思路
- 用自定义装饰器标记需要强制调用super的抽象方法
- 自定义元类继承
abc.ABCMeta,在子类定义时自动包装被标记方法:- 调用子类重写的方法前先重置调用标记
- 父类的被标记方法被调用时会自动设置标记
- 子类方法执行完成后检查标记,未设置则抛出异常
完整可运行代码示例
import abc import requests from functools import wraps # 标记需要强制调用super的装饰器 def require_super_call(func): func._require_super = True return func # 自定义元类,继承ABCMeta,添加super调用校验逻辑 class EnforceSuperCallMeta(abc.ABCMeta): def __new__(cls, name, bases, namespace): new_cls = super().__new__(cls, name, bases, namespace) # 遍历所有父类的属性 for base in bases: for attr_name, base_attr in base.__dict__.items(): # 仅处理被require_super_call标记的可调用方法 if callable(base_attr) and getattr(base_attr, '_require_super', False): # 包装父类方法,被调用时设置标记 @wraps(base_attr) def base_wrapper(self, *args, **kwargs): self._super_called = True return base_attr(self, *args, **kwargs) setattr(base, attr_name, base_wrapper) # 若子类重写了该方法,包装子类方法添加校验 if attr_name in namespace: sub_func = namespace[attr_name] @wraps(sub_func) def sub_wrapper(self, *args, **kwargs): self._super_called = False result = sub_func(self, *args, **kwargs) if not self._super_called: raise NotImplementedError( f"重写方法{attr_name}时必须显式调用super().{attr_name}()" ) return result setattr(new_cls, attr_name, sub_wrapper) return new_cls # 抽象父类使用自定义元类 class ParentCls(metaclass=EnforceSuperCallMeta): @abc.abstractmethod def login(self, username, password): """无super调用要求的抽象方法""" @require_super_call @abc.abstractmethod def download_file(self, resp: requests.Response) -> None: # 父类通用保存逻辑 print("执行父类通用的文件存储逻辑") pass class ChildCls(ParentCls): def login(self, username, password): # 不需要调用super,可正常执行 print("执行子类自定义登录逻辑") def download_file(self, link): # 子类自定义下载逻辑 dl_resp = requests.get(link, stream=True) # 注释下一行代码运行时会抛出异常 super().download_file(dl_resp) print("执行子类download_file收尾逻辑")
测试效果
if __name__ == "__main__": child = ChildCls() # 调用login方法无强制要求 child.login("test", "123456") # 调用download_file方法会校验super调用 child.download_file("https://example.com/test.zip")
如果子类download_file方法没有显式调用super().download_file(),运行时会直接抛出NotImplementedError提示缺失调用。
内容的提问来源于stack exchange,提问作者CaffeinatedMike
相关产品推荐
相关产品推荐

