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

Python 3.10装饰器困惑:如何包装类扩展__init__追踪函数调用

带参数的类装饰器实现方案

核心实现思路

带参数的类装饰器需要采用嵌套函数结构:外层函数接收装饰器的默认参数,内层函数作为真正的类装饰器,负责修改目标类的方法逻辑。同时要保证实例化时传入的参数优先级高于装饰器默认值,且保留原类方法的元信息。

完整代码实现

import functools

def keep_track(keep_track=True, do_print=True):
    def class_decorator(cls):
        # 保存原类的方法,避免覆盖后丢失逻辑
        original_init = cls.__init__
        original_call_me = cls.call_me

        # 包装__init__:添加参数处理逻辑
        @functools.wraps(original_init)
        def new_init(self, *args, **kwargs):
            # 实例化传参优先,否则用装饰器默认值
            self.keep_track = kwargs.pop('keep_track', keep_track)
            self.do_print = kwargs.pop('do_print', do_print)
            # 执行原__init__逻辑
            original_init(self, *args, **kwargs)
            # 仅当开启追踪时初始化调用计数
            if self.keep_track:
                self.call_count = 0

        # 包装call_me:添加打印、调用记录逻辑
        @functools.wraps(original_call_me)
        def new_call_me(self, *args, **kwargs):
            if self.do_print:
                print(f"[打印] call_me调用参数: {args}, {kwargs}")
            if self.keep_track:
                self.call_count += 1
                print(f"[记录] 第 {self.call_count} 次调用call_me")
            # 执行原call_me逻辑
            return original_call_me(self, *args, **kwargs)

        # 替换类中的方法
        cls.__init__ = new_init
        cls.call_me = new_call_me

        return cls
    return class_decorator

# 测试用的目标类
@keep_track(do_print=False)  # 装饰器默认关闭打印
class Foo:
    def __init__(self, name):
        self.name = name
        print(f"Foo实例初始化: name={self.name}")

    def call_me(self, message):
        print(f"原方法逻辑: {message}")

# 验证功能
if __name__ == "__main__":
    # 实例1:使用装饰器默认参数
    foo1 = Foo("Alice")
    foo1.call_me("Hello")
    foo1.call_me("Hi")
    print(f"foo1调用次数: {foo1.call_count}\n")

    # 实例2:实例化时覆盖装饰器参数
    foo2 = Foo("Bob", do_print=True, keep_track=False)
    foo2.call_me("Hello Bob")
    try:
        print(f"foo2调用次数: {foo2.call_count}")
    except AttributeError:
        print("foo2未开启调用记录")

关键细节说明

  1. 参数优先级处理:通过kwargs.pop从实例化参数中提取keep_track/do_print,优先使用实例化传参,否则 fallback 到装饰器默认值,避免干扰原__init__的参数逻辑。
  2. 元信息保留:用functools.wraps包装新方法,确保原方法的__name__、__doc__等元信息不丢失,避免破坏类的原有结构。
  3. 条件初始化:仅当keep_track=True时才创建call_count属性,避免给不需要追踪的实例添加冗余属性。

你之前可能踩的坑

  • 未使用嵌套函数结构:带参数的装饰器必须先接收参数,再返回真正的装饰器函数,直接写单层装饰器会导致参数传递逻辑混乱。
  • 未保存原方法:直接替换类方法会丢失原逻辑,必须先保存原方法引用,再在新方法中调用。
  • 参数处理错误:实例化传参未从kwargs中取出,导致原__init__接收到多余参数报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:57:52