2

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?

4 Answers 4

1

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

Sign up to request clarification or add additional context in comments.

3 Comments

oh, but then why a.attr2 works. maybes thats not it... good question
a.attr2 works precisely because attr2 doesn't exist and thus __getattr__ is invoked. And the default __getattribute__ obviously doesn't invoke __getattr__.
right, i follow. thanks for making it clear, i was second guessing myself for a minute
1
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.

Comments

0

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

2 Comments

I was under the impression that whenever one wants to override attribute access to do something other than the default, they should only override __getattr__() and not __getattribute__(). Is this correct? Are there any hidden side-effects from the implementation you are proposing above?
Please see the answer I contributed as well. I would appreciate any comments.
0

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: 

1 Comment

__getattribute__ is called for every non-magic method (__add__', '__repr__, etc.) so will have a big performance hit, as well as being completely unnecessary for what you want -- your original code using __getattr__ is appropriate for what you want to do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.