I read a bit on python\'s object attribute lookup (here: https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/#object-attribute-lookup).
Seems pretty stra
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:
__getattribute__
method through a direct search of the object's type's MRO, bypassing the regular attribute lookup process.__getattribute__
.
__getattribute__
returned something, the attribute lookup process is complete, and that's the attribute value.__getattribute__
raised a non-AttributeError, the attribute lookup process is complete, and the exception propagates out of the lookup.__getattribute__
raised an AttributeError. The lookup continues.__getattr__
method the same way we found __getattribute__
.
__getattr__
, the attribute lookup process is complete, and the AttributeError from __getattribute__
propagates.__getattr__
, and return or raise whatever __getattr__
returns or raises.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.