首页 > 解决方案 > 分析所有类方法

问题描述

我正在阅读Practical Python Design Patterns并且正在尝试学习装饰器的概念。我停留在最后一个示例中,我无法获得编写适用于类的所有方法的分析器的逻辑。

这是书中的示例。由于文案限制,我没有在这里重写它,但我希望 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

标签: pythondesign-patterns

解决方案


这段代码确实充满了错误。想想该__getattribute__方法的流程:给定一个属性名称,我们在包装类上查找该属性(通过调用超类实现)。如果在那里找不到该属性 - 在“fib”的情况下不是这样,因为它在包装的类上,而不是包装器上 - Python 将引发 AttributeError。好的,我们抓住了这一点,大概是这样我们就可以继续在包装类上查找它。但是我们在 except 子句中做了什么?没有什么。由于某种原因,该代码位于 else 子句中,该子句仅在引发异常时调用。

因此,如果我们通过从块中删除pass和移动代码来解决这个问题,那会怎样呢?else好吧,这现在想要得到self.inst,即被包装类的实例。但是你猜怎么着,获取属性会调用__getattribute__方法。所以我们递归。现在,获取inst属性的原始调用将成功。我们将其分配给x。怎么办?呃,没什么。我们退出而不返回 x。所以原始调用的值为 None self.inst,并尝试调用__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显式地装饰了,而代码的重点是方法将被自动装饰。


推荐阅读