我正在阅读Practical Python Design Patterns,并试图学习装饰器的概念。我停留在最后一个示例中,在该示例中,我无法获得编写适用于类的所有方法的探查器的逻辑。
Here is at the example from the book。由于复制限制,我没有在这里重写它,但我希望Google Book的链接足够。
问题是,当我实现代码并将其应用于我的DoMathStuff
类时,我得到TypeError: 'NoneType' object is not callable
。对我来说,try/except/else
部分尚不清楚,我认为某处有错字,但我可以分辨出哪里。
@profile_all_class_methods
class DoMathStuff(object):
"""docstring for DoMathStuff"""
def __init__(self, n):
self.n = n
def fib(self):
fPrev, f = 1, 1
for num in xrange(2, self.n):
fPrev, f = f, f + fPrev
return f
@profiling_decorator
def fact(self):
fct = 1
for num in xrange(1, self.n):
fct *= num
return fct
if __name__ == '__main__':
m = DoMathStuff(10)
print("Fib = {}, Fact = {}".format(m.fib(), m.fact()))
编辑:这是我遇到的错误
Traceback (most recent call last):
File "class_profiler.py", line 62, in <module>
print("Fib = {}, Fact = {}".format(m.fib(), m.fact()))
TypeError: 'NoneType' object is not callable
答案 0 :(得分:1)
这段代码确实充满了错误。只是考虑一下__getattribute__
方法的流程:给定一个属性名称,我们在包装器类上查找该属性(通过调用超类实现)。如果没有在该属性中找到属性(在“ fib”中则没有),因为它在包装的类上,而不是在包装器上,Python会引发AttributeError。好的,我们大概已经抓住了,以便我们可以继续在包装好的类上进行查找。但是在except子句中我们要做什么?没有。代码出于某种原因在else子句中,仅在 not 引发异常时才调用。
因此,如果我们通过移除pass
并将代码从else
块中移出来解决该问题,那该怎么办?好吧,这现在想要获取self.inst
,即包装类的实例。但是,请猜测,获取属性将调用__getattribute__
方法。所以我们递归。现在,获取inst
属性的原始调用将成功。我们将其分配给x
。怎么办?嗯,什么都没有。我们退出而不返回x。因此,原始调用将self.inst
的值设置为None,然后尝试对其调用__getattribute__
-这样我们将获得另一个AttributeError。
坦白说,这段代码看起来是由不太了解Python的人编写的。除了上述更改之外,还可以通过以下方法修复该问题:返回超类调用的值而不是为其分配值:
def __getattribute__(self, s):
try:
return super(ProfiledClass, self).__getattribute__(s)
except AttributeError:
x = self.inst.__getattribute__(s)
if type(x) == type(self.__init__):
return profiling_decorator(x)
else:
return x
但这仍然是很糟糕的代码。首先,永远不要直接调用双下划线方法,因此,except之后的行应为x = getattr(self.inst, s)
。但是问题比这更深。首先__getattribute__
是完全错误的方法。所有属性查找都调用该方法,因此需要复杂的try / super / except东西。但是Python给您提供了一种仅在直接找不到属性__getattr__
时才调用的方法。定义它可以使您完全删除大部分代码:
def __getattr__(self, s):
x = getattr(self.inst, s)
if type(x) == type(self.__init__):
return profiling_decorator(x)
else:
return x
(如果我真的很挑剔,我会只用type(x) == type(self.__init__)
来代替if callable(x)
的东西。)
此代码中的最后一个错误是,他们factorial
被显式修饰,而代码的全部重点是方法将被自动修饰。