分析所有类方法

时间:2018-11-29 11:04:33

标签: python design-patterns

我正在阅读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

1 个答案:

答案 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被显式修饰,而代码的全部重点是方法将被自动修饰。