python - Python中基于类的装饰器中存储的信息
问题描述
我是 Python 的初学者,从 Lutz 的书中学习装饰器。我在下面遇到了这段代码。tracer
即使创建了新实例,我也不确定为什么以及如何保留函数调用的数量。
class tracer:
def __init__(self,func):
self.calls=0
self.func=func
def __call__(self, *args):
self.calls+=1
print('call %s to %s' %(self.calls,self.func.__name__))
self.func(*args)
@tracer
def spam(a,b,c):
print (a+b+c)
spam(1,2,3) #Here calls counter increments to 1
t= tracer(spam)
t.func(3,4,5) # here calls counter increments to 2 even though I called `spam` using new instance `t`
g=tracer(spam)
g.func(4,5,6) #calls counter increments to 3.
正如我们在上面看到的,calls
即使创建了新实例,也会保留计数器状态。
有人可以解释为什么会这样吗?我尝试使用 PyCharm 调试代码,似乎内存位置spam
保持不变,与特定实例的调用无关。
我正在使用来自 Anaconda Distribution 的 Python 3.6。
解决方案
实际上,虽然 和 的赋值t
确实g
在创建新实例,但您传递的是原始包装函数的相同实例,spam
. 修饰后,spam
不再是函数,而是tracer
. 这就是 Python 设计处理对象周围类包装的方式:对象的名称成为包装对象的实例。
Anytimetracer(spam)
被创建,属性func
intracer
是原始包装函数的一个实例spam
。因此,当调用包装的值时,self.func(*args)
会调用 in tracer.__call__
,触发func.__call__
,它会递增calls
。
tracer
,t
和,的实例g
都被传递给 , 的同一个实例tracer
,spam
该实例被分配给 属性func
。因此,t.func
和g.func
都是实例,因此是引用spam
,以及它的所有属性。因此,当您调用t.func
and时g.func
,您正在触发,因此在 中spam.__call__
递增:calls
spam
class tracer:
def __init__(self, _func):
self.func = _func
self.calls = 0
def __repr__(self):
return f"{self.__class__.__name__}(storing {self.func.__name__})"
def __call__(self, *args):
self.calls += 1
print(f"__call__ executed in {repr(self)}")
return self.func(*args)
@tracer
def spam(a,b,c):
print (a+b+c)
>>>spam(1, 2, 3)
__call__ executed in tracer(storing spam)
t= tracer(spam)
>>>t.func(1, 2, 3)
__call__ executed in tracer(storing spam)
g=tracer(spam)
>>>g.func(1, 2, 3)
__call__ executed in tracer(storing spam)
推荐阅读
- python - 检查熊猫数据框列中的值是否为整数,如果不是则将其写入列表
- php - 如何正确地将我的 PHP 文件链接到我的 CSS
- python - 如何确定字节是 utf-8 还是 utf-16
- python - 替换或交换 3 个列表以创建单个列表/数组
- r - 如何在我的热图上分配 9 列特定颜色的独立渐变?
- c# - 如何解决:下载多个电子表格文件时,该进程无法访问文件“**”,因为它正被另一个进程使用
- django - Embed plotly:dash figure inside django template
- javascript - iPhone / iPad - 终止应用程序时未保存 Safari 浏览器 cookie
- node.js - 为什么我的快速路由器中间件没有定义 req.route?
- vue.js - 事件高度,如使用 vue-cal 的谷歌日历