This example of yours has fallen into some difficult ways to understand and explain.
And the reason why is that you replace the __getattribute__
- this is a method
very delicate, because it really involves all the search for attributes
of the object - including those made internally by the method __dir__
.
However, contrary to what the other reply comments, the problem does not
is just the attribute __class__
- most of these __class__
the Python
does internally, in native code, and does not go through __getattribute__
. Within the
__dir__
however, yes, it uses the mechanism of __getattribute__
to arrive
the class to object. To see the difference between the two, in an instance of its class Frob
original make print (type(Frob(0)))
, and then print(Frob(0).__class__
- the type
get the __class__
directly, without using the mechanism of __getattribute__
.
Understanding exactly what is happening is complicated. Ideally you create the method __getattr__
,and not __getattribute__
unless you know very well what you’re doing. The __getattr__
is only called when Python has not found an attribute in any other path. (Including the call itself to __getattr__
is made within the __getattribute__
original.
If it replaces the __getattribute__
, the best is to intervene as little as possible - exchange the attributes you want, in an "if" only seeing the desired attributes, and delegate the rest of the attributes to the __getattribute__
original - and not the other way around.
If you put a print
within the __getattribute__
will see that he is called by __dir__
to obtain __class__
and __dict__
- why this is the normal implementation of dir
: it shows the class attributes, and the instance attributes of an object - and the normal instance attributes are stored inside the __dict__
.
Then, note how, when returning __dict__
and __class__
of __getattribute__
you have listed the attributes set in the instance:
class Frob(object):
def __init__(self, bamf):
self.bamf=bamf
self.teste = 5
def __getattribute__(self,atr):
print(atr)
if atr in ("__class__", "__dict__"):
return super().__getattribute__(atr)
return object.__getattribute__(self,'teste')
In the terminal:
In [54]: print(dir(Frob(1)))
__dict__
__class__
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bamf', 'teste']
Summary of the mechanism of __getattribute__
If it’s about-writing __getattribute__
have to do carefully - and remember that in this code is done the search for normal Python attributes, which is reasonably complex:
- it first checks if the attribute exists in the object class (and does so using "type", not
obj.__class__
, that would be recursive:
- If the attribute exists in class it checks if this attribute is an object that has the methods
__get__
and __set__
(a "data Descriptor"):
- If yes (but not only the
__get__
and does not have the __set__
) - he calls the method __get__
of the attribute, passing the instance (self) and the class (type(self)) and returns that value.
- following, the
__getattribute__
checks if the object has a __dict__
(no recursiveness, using internal mechanisms), if yes, checks if the attribute exists in this __dict__
and returns this value. (the __dict__
of an object has to be a real Python dictionary, and if you try to put a subclass that changes the __getitem__
, this special method will be ignored)
- The
__getattribute__
checks if the object class has __slots__
and return the slot value corresponding to the name
- The
__getattribute__
checks if the value exists in the object class.
- if it exists and has the method
__get__
, this method is called, with the instance and the class (the normal methods of objects are created at that point), and returns the value obtained.
- else only returns the attribute found in the class. (the search for class values in that item (4) and item (1) nay calls the method
__getattribute__
of the object’s class (type(self)), it simply looks at the __dict__
of the class, and proceeds, looking at the __dict__
of the super-classes (the linear order of the super-classes, which is always in the attribute __mro__
class).
- If attribute not yet found, if method exists
__getattr__
he is called. If the __getattr__
return a value, it is returned. If the __getattr__
raise an exception of AttributeError
, the __getattribute__
with the search for us __getattr__
of the super-classes of the object.
- The
__getattribute__
raises the exception of AttributeError
.
So this is it - this complexity is what makes Python so simple and easy to use in the end- why does he do "what we expect him to do", even if we don’t stop to think that all this happens behind the scenes.
And also, because of this complexity, they invented the sister method __getattr__
, that can be customized much more simply.
Thanks brother, now it made sense to me!
– Erick Kokubum
I even thought your answer was wrong, because. is very strange
dir
internal use of normal access bygetattribute
of the object - but in fact this is what happens. I put another answer complementing what the__getattribute__
does indeed.– jsbueno
(by the way - this is more an implementation detail than something specified in the language - it is worth looking for example what happens in pypy (another implementation of Python))
– jsbueno