Python运行时动态继承并修改父类未知方法的实现问题
问题描述
现有父类Parent,无法预先知晓其方法,这些方法可执行任意操作(如修改实例属性)。需求是动态创建子类,将父类每个方法修改为执行两次,并返回两次调用的结果列表,同时要求方法仍能像原方法一样修改实例属性。
静态实现可达成预期效果,但实际场景中仅能获取父类实例my_parent,其方法未知。尝试使用types.MethodType动态绑定修改后的方法,运行后方法返回结果符合预期,但实例属性未被正确修改。
相关代码示例
父类定义
class Parent(): def __init__(self): self.a = 42 def arbitrary_name1(self, foo, bar): self.a += 1 return foo + bar def arbitrary_name2(self, foo, bar): self.a -= 1 return foo ** 2 + bar my_parent = Parent()
静态实现示例
class MyChild(Parent): def __init__(self): super().__init__() def arbitrary_name1(self, foo, bar): list_of_res = [] for _ in range(2): list_of_res.append(super().arbitrary_name1(foo, bar)) return list_of_res def arbitrary_name2(self, foo, bar): list_of_res = [] for _ in range(2): list_of_res.append(super().arbitrary_name2(foo, bar)) return list_of_res
动态实现尝试代码(存在问题)
import re import types # 获取父类的所有方法 parent_methods_names = [method_name for method_name in dir(my_parent) if callable(getattr(my_parent, method_name)) and not re.match(r"__.+__", method_name)] def do_twice(function): def wrapped_function(self, foo, bar): res = [] for _ in range(2): res.append(function(foo, bar)) return res return wrapped_function my_dyn_child = Parent() for method_name in parent_methods_names: f = types.MethodType(do_twice(getattr(my_parent, method_name)), my_dyn_child) setattr(my_dyn_child, method_name, f) assert getattr(my_dyn_child, parent_methods_names[0])(1, 2) == [3, 3] assert my_dyn_child.a == 44 print("Success!")
问题原因
动态尝试失败的核心原因是:包装时直接调用了my_parent实例的绑定方法(getattr(my_parent, method_name)),这个方法已经和my_parent实例绑定,执行时修改的是my_parent的属性,而非目标实例my_dyn_child的属性。
解决方案
方案一:动态创建子类(推荐)
通过type函数动态生成子类,在类层面包装所有父类方法,确保方法自动绑定到子类实例的self。
class Parent(): def __init__(self): self.a = 42 def arbitrary_name1(self, foo, bar): self.a += 1 return foo + bar def arbitrary_name2(self, foo, bar): self.a -= 1 return foo ** 2 + bar my_parent = Parent() def wrap_method(method): def wrapped(self, *args, **kwargs): results = [] for _ in range(2): # 传入当前实例的self调用原方法 results.append(method(self, *args, **kwargs)) return results return wrapped # 从父类中获取非魔法方法(避免绑定到特定实例) parent_methods = { name: method for name, method in vars(Parent).items() if callable(method) and not name.startswith('__') } # 包装所有方法 wrapped_methods = {name: wrap_method(method) for name, method in parent_methods.items()} # 动态创建子类:类名、父类元组、类属性字典 DynamicChild = type('DynamicChild', (Parent,), wrapped_methods) # 测试验证 my_dyn_child = DynamicChild() assert my_dyn_child.arbitrary_name1(1, 2) == [3, 3] assert my_dyn_child.a == 44 # 两次调用,a从42变为44 assert my_dyn_child.arbitrary_name2(3, 4) == [13, 13] assert my_dyn_child.a == 42 # 两次调用,a从44减回42 print("Success!")
该方案符合面向对象设计,子类可复用,所有子类实例都会自动拥有包装后的方法。
方案二:直接修改现有实例的方法
如果不需要创建子类,仅需修改单个实例的方法,可在包装函数中动态获取当前实例的原方法并传入self执行。
import re import types class Parent(): def __init__(self): self.a = 42 def arbitrary_name1(self, foo, bar): self.a += 1 return foo + bar def arbitrary_name2(self, foo, bar): self.a -= 1 return foo ** 2 + bar my_parent = Parent() def do_twice(method_name): def wrapped_function(self, *args, **kwargs): res = [] # 从实例的类中获取原方法(未绑定的函数) original_method = getattr(type(self), method_name) for _ in range(2): # 传入当前实例的self执行原方法 res.append(original_method(self, *args, **kwargs)) return res return wrapped_function my_dyn_child = Parent() # 获取实例的非魔法方法 parent_methods_names = [ method_name for method_name in dir(my_dyn_child) if callable(getattr(my_dyn_child, method_name)) and not re.match(r"__.+__", method_name) ] # 为实例绑定包装后的方法 for method_name in parent_methods_names: f = types.MethodType(do_twice(method_name), my_dyn_child) setattr(my_dyn_child, method_name, f) # 测试验证 assert my_dyn_child.arbitrary_name1(1, 2) == [3, 3] assert my_dyn_child.a == 44 assert my_dyn_child.arbitrary_name2(3, 4) == [13, 13] assert my_dyn_child.a == 42 print("Success!")
该方案更灵活,适合仅需修改单个实例的场景,不会影响其他实例或类本身。
内容的提问来源于stack exchange,提问作者jeandut
相关产品推荐
相关产品推荐

