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

Python不修改派生类时 基类包装派生类方法的内存优化方案

问题背景

假设存在如下基类与派生类定义:

class A:
    def foo(self):
        pass

class B(A):
    def foo(self):
        pass

需求:对B类实例发起的foo方法调用进行包装,不允许修改B类的任何代码(B类不属于我方维护范畴)。

当前实现的问题

现有实现方案如下:

class A:
    def __init__(self):
        fnames = ["foo"]
        for fname in fnames:

            def capture(f):
                def wrapper(*args, **kwargs):
                    print("wrapped")
                    return f(*args, **kwargs)

                return wrapper

            meth = capture(getattr(self, fname))
            bound = meth.__get__(self)
            setattr(self, fname, bound)


class B(A):
    def foo(self):
        print("b")


o = B()
o.foo()

print(o.foo)

该方案可以实现预期的包装效果,但存在明显的内存效率问题:当前实现中o.foo为<bound method A.__init__.<locals>.capture.<locals>.wrapper of <__main__.B object at 0x10523ffd0>>,每创建一个实例都会生成2层闭包,当实例量级较大时会产生大量冗余内存开销。

更优实现方案

不需要使用元类,以下两种方案都可以避免逐实例生成闭包的内存浪费:

方案1:类级别统一包装(推荐,性能最优)

直接在A类层面替换需要包装的方法,包装逻辑仅在类定义时生成一次,所有实例共用,无额外实例内存开销:

from functools import wraps

class A:
    def foo(self):
        pass

# 类级别包装,仅执行1次
_origin_a_foo = A.foo
@wraps(_origin_a_foo)
def wrapped_foo(self, *args, **kwargs):
    print("wrapped")
    # 沿MRO链调用子类的原始foo实现
    return super(type(self), self).foo(*args, **kwargs)
A.foo = wrapped_foo


class B(A):
    def foo(self):
        print("b")


# 测试
o = B()
o.foo()  # 依次输出 wrapped、b
print(o.foo)  # 输出 <bound method A.wrapped_foo of <__main__.B object at 0x...>>

这个方案完全没有修改B类代码,符合需求限制。原理是Python方法调用遵循MRO(方法解析顺序)规则,替换A类的foo为包装版本后,B实例调用foo时会优先命中A类上的包装逻辑,包装内部通过super调用MRO链中A类之后的实现(也就是B类自定义的foo),行为和预期完全一致,是所有方案中运行性能、内存效率最高的实现。

方案2:getattribute 拦截(适合动态配置场景)

如果需要包装的方法列表需要动态调整,可以在A类重写__getattribute__做统一拦截,同样不需要在__init__中为每个实例生成闭包:

# 可动态修改的要包装的方法集合
WRAPPED_METHODS = {"foo"}

class A:
    def foo(self):
        pass
    def __getattribute__(self, name):
        attr = super().__getattribute__(name)
        if name in WRAPPED_METHODS and callable(attr):
            def wrapper(*args, **kwargs):
                print("wrapped")
                return attr(*args, **kwargs)
            return wrapper
        return attr

class B(A):
    def foo(self):
        print("b")

该方案的缺点是每次实例属性访问都会触发__getattribute__的判断逻辑,运行性能略低于方案1,仅适合需要灵活调整包装方法的场景。

关于元类实现的说明

不推荐使用元类实现该需求:元类的作用是控制类的创建过程,本场景下我们不需要修改B类的定义本身,使用元类只会徒增代码复杂度,最终实现的效果和方案1没有区别,没有额外收益。


内容的提问来源于stack exchange,提问作者xrisk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:42:30