You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:12:13