Python父类如何检测被子类重写的方法及对应子类类名
实现方案
Python 原生的类元信息能力完全可以覆盖这两个检测需求,不需要额外第三方依赖,核心用到两个内置能力:
- 类的
__subclasses__()方法:可以获取当前类的所有直接继承子类,递归遍历就能拿到所有层级的子类 - 方法身份比对:如果子类重写了父类方法,子类持有的同名方法对象和父类原始方法对象不是同一个,直接用
is判断即可
两个检测需求的具体实现逻辑
1. 识别父类中被子类重写的方法
- 先提取父类自身定义的普通方法(过滤掉
__开头的内置特殊方法) - 递归遍历所有层级的子类,逐个比对同名方法和父类原始方法的身份,不一致就标记为被重写
- 去重后就能得到所有被重写过的父类方法列表
2. 获取重写了指定方法的所有子类类名
- 递归遍历当前父类的所有层级子类(包含间接继承的子类)
- 对每个子类,检查指定方法是否定义在子类自身的属性字典中,且该方法和父类原始方法身份不一致
- 收集所有符合条件的子类的
__name__属性即可
适配示例类结构的可运行代码
class Parent: @classmethod def get_all_subclasses(cls): """递归获取所有层级的子类""" subclasses = [] for subclass in cls.__subclasses__(): subclasses.append(subclass) subclasses.extend(subclass.get_all_subclasses()) return subclasses @classmethod def get_overridden_methods(cls): """获取父类中所有被子类重写过的方法名""" # 提取父类自身定义的普通方法 parent_methods = { method_name for method_name, method in cls.__dict__.items() if callable(method) and not method_name.startswith("__") } overridden = set() for subclass in cls.get_all_subclasses(): for method_name in parent_methods: # 子类自身定义了同名方法,且和父类方法不是同一个,判定为重写 if method_name in subclass.__dict__ and subclass.__dict__[method_name] is not cls.__dict__[method_name]: overridden.add(method_name) return list(overridden) @classmethod def get_subclasses_override_method(cls, target_method_name): """获取重写了指定方法的所有子类类名""" if target_method_name not in cls.__dict__ or not callable(cls.__dict__[target_method_name]): raise ValueError(f"父类中不存在名为{target_method_name}的可调用方法") result = [] for subclass in cls.get_all_subclasses(): if target_method_name in subclass.__dict__ and subclass.__dict__[target_method_name] is not cls.__dict__[target_method_name]: result.append(subclass.__name__) return result def call_api(self): print("API is called") def use_call_api(self): # 实例方法内判断当前实例所属类是否重写了call_api if type(self).call_api is not Parent.call_api: print("检测到call_api被子类重写,执行前置逻辑") self.call_api() else: print("call_api未被重写,执行默认逻辑") self.call_api() class Child(Parent): def call_api(self): print("call_api") class Child2(Parent): def call_api(self): print("call_api2") class Child3(Parent): def call_api(self): print("call_ap3") def use_call_api(self): print("custom call_api") # 测试效果 if __name__ == "__main__": print("父类中被重写的方法:", Parent.get_overridden_methods()) print("重写了call_api的子类:", Parent.get_subclasses_override_method("call_api")) print("重写了use_call_api的子类:", Parent.get_subclasses_override_method("use_call_api")) print("\n--- 父类实例调用use_call_api ---") p = Parent() p.use_call_api() print("\n--- Child实例调用use_call_api ---") c1 = Child() c1.use_call_api() print("\n--- Child3实例调用use_call_api ---") c3 = Child3() c3.use_call_api()
运行输出参考
父类中被重写的方法: ['use_call_api', 'call_api'] 重写了call_api的子类: ['Child', 'Child2', 'Child3'] 重写了use_call_api的子类: ['Child3'] --- 父类实例调用use_call_api --- call_api未被重写,执行默认逻辑 API is called --- Child实例调用use_call_api --- 检测到call_api被子类重写,执行前置逻辑 call_api --- Child3实例调用use_call_api --- custom call_api
注意事项
- 不要用
hasattr()判断方法是否重写,子类会继承父类方法,hasattr()永远返回True,必须直接检查子类自身的__dict__是否存在同名方法,再做身份比对 - 多层继承场景(比如Child派生出GrandChild类)下,上述递归获取子类的逻辑可以正常覆盖
- 实例方法内判断当前实例是否重写方法,必须用
type(self)取实例所属类来比对方法,不要直接用self.方法名,绑定方法每次访问都会生成新对象,身份比对会出错
内容的提问来源于stack exchange,提问作者Seyed Mostafa SeyedAshoor
相关产品推荐
相关产品推荐

