覆盖__getattr__导致递归

时间:2019-01-11 13:17:41

标签: python 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被多次打印。我想要什么?

2 个答案:

答案 0 :(得分:1)

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,但是如果_foo也是计算属性,也会破坏继承-是否存在问题取决于上下文和具体的用例。

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

注意:我假设您将foo设为计算属性,以允许_foo的延迟初始化,否则就没有意义了。如果是这种情况,您还可以非常简单地在初始化器中创建一个带有前哨值的_foo(通常为None,除非None是此属性的有效值)并提供它在foo()中是真正的价值。

答案 1 :(得分:1)

您最初需要将_foo设置为某种值,即使它不是真正的值。像这样:

class A(object):
    _foo = None

    def __init__(self):
        pass

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

    @property
    def foo(self):
        if self._foo is None:
            self._foo = [2, 1]
        return self._foo