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

Python中两次使用不同装饰器后函数属性丢失的原因及访问疑问

装饰器属性“丢失”的原因及内存状态解析

为什么call_count属性无法通过当前fib访问?

你执行fib = count(fib)后,fib指向的是count装饰器返回的counted包装函数,这个函数带有call_count属性。但紧接着fib = count_frames(fib),此时fib被重新赋值为count_frames返回的全新包装函数——也就是第二个装饰器生成的counted对象。

现在你访问的fib.__dict__是第二个包装函数的属性字典,第一个装饰器的counted对象的call_count不属于这个新对象,所以自然不会出现在结果里。

call_count属性是否还在内存中?

是的,它依然存在。因为第二个装饰器的counted函数内部通过闭包持有对第一个装饰器返回的counted函数的引用(也就是count_frames的参数f,这个f就是第一次装饰后的fib)。只要这个引用没被销毁,第一个counted对象就不会被Python垃圾回收,它的call_count属性也会保留。

如何验证并访问这个属性?

有两种简单方法:

  • 方法一:保存第一次装饰后的函数引用
    修改代码,把第一次装饰的结果单独存起来:

    def fib(n):
        if n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            return fib(n - 2) + fib(n - 1)
    
    def count(f):
        def counted(n):
            counted.call_count += 1
            return f(n)
        counted.call_count = 0
        return counted
    
    def count_frames(f):
        def counted(n):
            counted.open_count += 1
            counted.max_count = max(counted.max_count, counted.open_count)
            result = f(n)
            counted.open_count -= 1
            return result
        counted.open_count = 0
        counted.max_count = 0
        return counted
    
    # 保存第一次装饰后的函数
    fib_counted = count(fib)
    fib = count_frames(fib_counted)
    
    print(fib(19), fib.__dict__)
    # 直接访问第一个装饰器的call_count
    print("第一个装饰器的call_count:", fib_counted.call_count)
    
  • 方法二:从第二个装饰器的闭包中提取引用
    不需要额外保存变量,通过函数的__closure__属性获取内部引用:

    # 原代码执行后
    fib(19)
    # 从闭包中取出第一个装饰后的函数(索引根据闭包变量顺序调整,这里f是第一个变量)
    first_decorated = fib.__closure__[0].cell_contents
    print("第一个装饰器的call_count:", first_decorated.call_count)
    

内容的提问来源于stack exchange,提问作者joker-Dmmm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:22:06