Uri Laserson
Uri Laserson

Reputation: 2451

Python attribute access error

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 an AttributeError.

So if __getattribute__() is raising an AttributeError, then why isn't __getattr__() being called and returning the proper value?

Upvotes: 2

Views: 2047

Answers (4)

Uri Laserson
Uri Laserson

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

Winston Ewert
Winston Ewert

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

Lauritz V. Thaulow
Lauritz V. Thaulow

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

jon_darkstar
jon_darkstar

Reputation: 16768

__getattribute__() is still default, so it is reaching for self.attr2 instead of self.data['attr2']

Upvotes: 1

Related Questions