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
相关产品推荐
相关产品推荐

