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

如何利用父类定义的装饰器保存函数每次调用的参数

问题背景与疑问

现有A、B父子类结构,用户可继承任意类定义子类Z,其initialize方法可继承父类实现或自行重写。目前每次调用initialize时需自动保存参数信息——原来的做法是在Z.initialize()里显式调用save_last_arginfo(),现在要改成用A类中定义的装饰器自动实现,有以下几个问题:

  1. 如何编写该装饰器;
  2. 装饰器定义及@语法中self的处理方式;
  3. 如何引用该装饰器(是否需指定A.decorator);
  4. 使用装饰器后,inspect.getouterframes的帧层级是否会发生变化。
解决方案与说明

1. 装饰器的正确写法

不能把装饰器写成普通实例方法——因为装饰类方法时,类的实例还没创建呢。得把它写成类方法,这样既能访问类结构,又能在装饰阶段被正常调用。

装饰器核心逻辑是包装目标方法:先调用原方法,再触发save_last_arginfo保存参数(你也可以根据需求反过来,先保存再调用)。一定要用functools.wraps把原方法的元信息拷贝到包装函数里,不然inspect获取参数时会拿到包装函数的信息,而非原方法的。

2. self的处理方式

装饰器作为类方法,第一个参数是cls(代表类本身);而被装饰的initialize是实例方法,第一个参数是self(代表实例对象)。在装饰器的包装函数里,我们能直接拿到这个self,直接调用它的save_last_arginfo方法即可,不需要额外的特殊处理。

3. 装饰器的引用方式

因为装饰器定义在父类A中,子类B、Z直接使用@A.decorator是最稳妥的方式,不会出现作用域查找问题。如果子类继承了A,理论上用@decorator也能生效,但万一子类中出现同名方法就会出问题,所以推荐写全A.decorator。

4. 帧层级的变化

使用装饰器后,调用栈会多一层包装函数的调用,所以inspect.getouterframes的帧层级确实会变。原代码中用[1]获取外层帧,现在需要改成[2]才能跳过装饰器的包装层,拿到真正调用initialize的原始帧。

修正后的完整代码
import inspect
import functools

class A():
    lastargsinfo = None

    def initialize(self, *args, **kwargs):
        pass

    def save_last_arginfo(self):
        # 获取initialize方法的参数规范
        argspec = inspect.getfullargspec(self.initialize)
        args1, varargs1, varkw1, defaults1 = argspec[:4]
        
        # 装饰器增加了一层调用栈,所以取索引[2]而非原[1]
        frame = inspect.getouterframes(inspect.currentframe())[2][0]
        args2, varargs2, keywords2, locals2 = inspect.getargvalues(frame)
        
        self.lastargsinfo = [args1, defaults1, locals2]

    @classmethod
    def decorator(cls, func):
        @functools.wraps(func)
        def wrapper(self, *args, **kwargs):
            # 先执行原initialize方法,再保存参数信息
            result = func(self, *args, **kwargs)
            self.save_last_arginfo()
            return result
        return wrapper

class B(A):
    @A.decorator
    def initialize(self, a, b=1):
        pass

class Z(B):
    @A.decorator
    def initialize(self, a, b=1, c=2, **kwargs):
        # 移除显式调用,由装饰器自动处理参数保存
        return (a + b) * c

# 测试代码
myZ = Z()
myZ.initialize(0, b=3, c=8, d=12)
print(myZ.lastargsinfo)
# 输出示例:[['self', 'a', 'b', 'c'], (1, 2), {'self': <__main__.Z object at 0x000001BBBF135340>, 'a': 0, 'b': 3, 'c': 8, 'kwargs': {'d': 12}}]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:40:19