Why does Foo2 result in infinite recursion calling getattr
of a class variable in __getattribute__
, but Foo works fine making the same call in __getattr__
? Any suggestions on how to get Foo2 to work?
class Foobar(object):
def __init__(self):
super().__init__()
self.bar = 5
def getbar(self):
return self.bar
class Foo(object):
def __init__(self):
super().__init__()
self.__foo = Foobar()
def __getattr__(self, attr):
return getattr(self.__foo, attr)
class Foo2(object):
def __init__(self):
super().__init__()
self.__foo = Foobar()
def __getattribute__(self, attr):
try:
return getattr(self.__foo, attr)
except AttributeError:
super().__getattribute__(attr)
if __name__ == '__main__':
foo = Foo()
foo2 = Foo2()
print(foo.bar, foo.getbar()) # Works as expected
try:
print(foo2.bar, foo2.getbar()) # Doesn't work
except RecursionError:
print('Why does Foo2 result in RecursionError. How to fix?')
Setup: Windows 10, Python 3.7
The __getattribute__
method is called unconditionally to look up all attributes on an object, not only ones that don't exist (which is what __getattr__
does). When you do self.__foo
in its implementation, you recurse, since __foo
is another attribute that we're trying to look up on the object.
To avoid this issue, you need to call your parent's __getattribute__
method to get all of your own attributes inside the __getattribute__
method:
def __getattribute__(self, attr):
try:
return getattr(super().__getattribute__("_Foo__foo"), attr)
except AttributeError:
super().__getattribute__(attr)
Note that I had to manually apply name mangling to the __foo
attribute, because we need to pass the name as a string to super().__getattribute__
. That probably suggests you shouldn't be doing the mangling in the first place. A name with a single leading underscore might be a better choice.