我是Python的初学者,从Lutz的书中学习Decorators。我在下面遇到了这个代码。我不确定为什么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发行版的python3.6
您的问题是
t
和g
是双重包装的跟踪器。也就是说,它们是一个tracer
实例,包含另一个tracer
实例(它最后引用了一个函数)。外部的tracer
实际上不能正常工作,因为内部的tracer
没有__name__
属性,因为它不是一个函数。您只能调用t.func
,它绕过外部跟踪程序(及其计数)直接调用内部跟踪程序通过向每个
tracer
添加__name__
属性,可以使代码正常工作:现在您可以调用
t(3, 4, 5)
和g(5, 6, 7)
,每个调用将输出两个计数,一个用于内部跟踪程序,一个用于外部跟踪程序。外部的计数是分开的(每个从1开始),但是内部的计数是共享的(就像你最初看到的那样)当然,这可能是因为您不需要嵌套的跟踪器。在这种情况下,您可能需要删除函数前面的
@tracer
行。这就是应用内部跟踪器的地方,它相当于将spam = tracer(spam)
放在spam
函数的定义之后。没有这一行,spam
将直接引用函数(没有tracer
环绕它),并且t
和g
将直接应用于函数,而没有内部的tracer
妨碍实际上,当
t
和g
的赋值确实在创建新实例时,您传递的是原始包装函数的相同实例spam
。经过修饰后,spam
不再是函数,而是tracer
的实例。这就是Python如何设计来处理围绕对象的类包装:对象的名称成为包装对象的实例每当创建
tracer(spam)
时,tracer
中的属性func
就是原始包装函数spam
的实例。因此,在调用包装值时,self.func(*args)
在tracer.__call__
中被调用,触发func.__call__
,它增加calls
tracer
、t
和g
的实例都被传递给tracer
、spam
的相同实例,该实例被分配给属性func
。因此,t.func
和g.func
都是实例,因此引用spam
,以及它的所有属性。因此,当您调用t.func
和g.func
时,您将触发spam.__call__
,从而在spam
中增加calls
:相关问题 更多 >
编程相关推荐