How do I implement __getattribute__ without an infinite recursion error?
Python language reference:
In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example,
object.__getattribute__(self, name)
.
Meaning:
def __getattribute__(self,name):
...
return self.__dict__[name]
You're calling for an attribute called __dict__
. Because it's an attribute, __getattribute__
gets called in search for __dict__
which calls __getattribute__
which calls ... yada yada yada
return object.__getattribute__(self, name)
Using the base classes __getattribute__
helps finding the real attribute.
You get a recursion error because your attempt to access the self.__dict__
attribute inside __getattribute__
invokes your __getattribute__
again. If you use object
's __getattribute__
instead, it works:
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name=='test':
return 0.
else:
return object.__getattribute__(self, name)
This works because object
(in this example) is the base class. By calling the base version of __getattribute__
you avoid the recursive hell you were in before.
Ipython output with code in foo.py:
In [1]: from foo import *
In [2]: d = D()
In [3]: d.test
Out[3]: 0.0
In [4]: d.test2
Out[4]: 21
Update:
There's something in the section titled More attribute access for new-style classes in the current documentation, where they recommend doing exactly this to avoid the infinite recursion.
Actually, I believe you want to use the __getattr__
special method instead.
Quote from the Python docs:
__getattr__( self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self).
name
is the attribute name. This method should return the (computed) attribute value or raise anAttributeError
exception.
Note that if the attribute is found through the normal mechanism,__getattr__()
is not called. (This is an intentional asymmetry between__getattr__()
and__setattr__()
.) This is done both for efficiency reasons and because otherwise__setattr__()
would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the__getattribute__()
method below for a way to actually get total control in new-style classes.
Note: for this to work, the instance should not have a test
attribute, so the line self.test=20
should be removed.