pythonpython-3.xattributesgetattrgetattribute

Python 3 __getattribute__ vs dot access behaviour


I read a bit on Python's object attribute lookup:

Seems pretty straight forward, so I tried it out (python3):

class A:
    def __getattr__(self, attr):
        return (1,2,3)

a = A()

a.foobar #returns (1,2,3) as expected
a.__getattribute__('foobar') # raises AttributeError

My question is, aren't the two supposed to be identical?

Why does the second one raise an attribute error?

So apparently the answer is that the logic for a.foobar IS different from the logic for a.__getattribute("foobar"). According to the data model: a.foobar calls a.__getattribute("foobar") and if it raises an AttributeError, it calls a.-__getattr__('foobar')

So it seems the article has a mistake in their diagram. Is this correct?

And another question: Where does the real logic for a.foobar sit? I thought it was in __getattribute__ but apparently not entirely.

Edit:
Not a duplicate of Difference between __getattr__ and __getattribute__.

I am asking here what is the different between object.foo and object.__getattribute__("foo").
This is different from __getattr__ vs __getatribute__ which is trivial...


Solution

  • It's easy to get the impression that __getattribute__ is responsible for more than it really is. thing.attr doesn't directly translate to thing.__getattribute__('attr'), and __getattribute__ is not responsible for calling __getattr__.

    The fallback to __getattr__ happens in the part of the attribute access machinery that lies outside __getattribute__. The attribute lookup process works like this:

    At least, in terms of the language semantics, it works like that. In terms of the low-level implementation, some of these steps may be optimized out in cases where they're unnecessary, and there are C hooks like tp_getattro that I haven't described. You don't need to worry about that kind of thing unless you want to dive into the CPython interpreter source code.