Reputation: 2451
I am trying to customize attribute access in a class (python2.7). Say I define the following class:
class testAttrAccess(object):
def __init__(self):
self.data = {"attr1":"value1","attr2":"value2"}
def __getattr__(self,name):
try:
return self.data[name]
except KeyError:
pass
raise AttributeError
Then I get:
In [85]: a = testAttrAccess()
In [86]: a.attr2
Out[86]: 'value2'
In [87]: a.__getattr__('attr2')
Out[87]: 'value2'
In [88]: a.__getattribute__('attr2')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/laserson/temp/<ipython console> in <module>()
AttributeError: 'testAttrAccess' object has no attribute 'attr2'
However, according to the python documentation
If the class also defines
__getattr__()
, the latter will not be called unless__getattribute__()
either calls it explicitly or raises anAttributeError
.
So if __getattribute__()
is raising an AttributeError
, then why isn't __getattr__()
being called and returning the proper value?
Upvotes: 2
Views: 2047
Reputation: 2451
Inspired by @lazyr, this solution seems to work too. It seems simpler to me...does it raise any red flags to anyone?
class testAttrAccess(object):
def __init__(self):
self.data = {"attr1":"value1","attr2":"value2"}
def __getattribute__(self,name):
try:
return object.__getattribute__(self,name)
except AttributeError:
pass
try:
return self.data[name]
except KeyError:
pass
raise AttributeError
The following session gives:
In [108]: a = testAttrAccess()
In [109]: a.attr2
Out[109]: 'value2'
In [110]: a.__getattribute__('attr2')
Out[110]: 'value2'
In [111]: a.__getattr__('attr2')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/laserson/temp/<ipython console> in <module>()
/Users/laserson/temp/<ipython console> in __getattribute__(self, name)
AttributeError:
In [112]: a.qwerty
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/Users/laserson/temp/<ipython console> in <module>()
/Users/laserson/temp/<ipython console> in __getattribute__(self, name)
AttributeError:
Upvotes: 0
Reputation: 45049
So if __getattribute__() is raising an AttributeError, then why isn't __getattr__() being called and returning the proper value?
That only applies if python is calling __getattribute__
for you as a part of normal attribute access, not if you call __getattribute__
yourself. Basically, you should almost never call a python method of the form __foo__
Such methods are used to implement behaviors depended on by other operations.
Upvotes: 1
Reputation: 50995
a.__getattr__
gets called only if the attribute is not found in a.__dict__
.
a.__getattribute__
is called in all cases, if it is defined.
As you know, a.__getattribute__(attr)
is syntactic sugar for type(a).__getattribute__(a, attr)
, but since this method is not defined in testAttrAccess
, the parent method object.__getattribute__(a, attr)
is called. From your results we can then conclude that it is not object.__getattribute__
that calls __getattr__
. Probably the interpreter takes care of that.
You can make your program work by changing it like this:
class testAttrAccess(object):
def __init__(self):
self.data = {"attr1": "value1", "attr2": "value2"}
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except AttributeError:
if name == "__getattr__":
return object.__getattribute__(self, "__getattribute__")
try:
return object.__getattribute__(self, "data")[name]
except KeyError:
raise AttributeError
a = testAttrAccess()
print a.attr2
print a.__getattr__('attr2')
print a.__getattribute__('attr2')
print a.qwerty
Output:
value2 value2 value2 Traceback (most recent call last): File "test.py", line 20, in print a.qwerty File "test.py", line 13, in __getattribute__ raise AttributeError AttributeError
Upvotes: 0
Reputation: 16768
__getattribute__()
is still default, so it is reaching for self.attr2
instead of self.data['attr2']
Upvotes: 1