首页 > 解决方案 > 覆盖 __getattr__ 导致递归

问题描述

我正在尝试创建一个包装类,然后覆盖__getattr__,以便对不在包装类中的属性的请求传递到内部对象中。下面是一个玩具示例,我使用列表作为内部对象 -

class A(object):
    def __init__(self):
        pass

    def __getattr__(self, name):
        print 'HERE'
        return getattr(self.foo, name)

    @property
    def foo(self):
        try:
            return self._foo
        except:
            self._foo = [2, 1]
            return self._foo 

我想做类似的事情

 a = A()
 a.sort()  # initializes and sorts _foo 
 print a.foo

哪个打印[1, 2]

最后,我需要在第一次调用_foo时进行初始化,foo或者当有人试图从中获取A()不在A. 不幸的是,我正在进行某种递归并且HERE被打印了很多次。我想要的可能吗?

标签: pythongetattr

解决方案


这个

return getattr(self.foo, name)

实际上是:

foo = self.foo
return getattr(foo, name)

现在self.foo实际上称之为:

try:
   return self._foo

其中,由于您的班级定义了__getattr__并且 - 至少第一次调用self.foo- 没有_foo属性,调用self.__getattr__("_foo"),调用self.foo,等等等等等等......

简单的解决方案:确保self._foo在之前定义(即在初始化程序中),而不是尝试在foo().

您还可以测试_foo实例的dict中是否存在,但是如果它也是一个计算属性,那么这将破坏继承_foo——这是否是一个问题取决于上下文和具体用例。

@property
def foo(self):
    if '_foo' not in self.__dict__:
        self._foo = [2, 1]
    return self._foo 

注意:我假设您创建了foo一个计算属性以允许延迟初始化_foo,否则它没有什么意义。如果是这种情况,您也可以非常简单地_foo在初始化程序中创建一个标记值(通常None,除非None是此属性的有效值),并在foo().


推荐阅读