This is my class:
class User():
...
def __getattribute__(self, name):
if name not in ['create_user', 'authenticate_user']:
x = super().__getattribute__('authenticate_user')
print(x)
return super().__getattribute__(name)
I want to get the value of the method authenticate_user
, but I can't.
In this part:
x = super().__getattribute__('authenticate_user')
I only get the method, but I want to execute it in order to obtain the value that the method returns. But when you run it:
x = super().__getattribute__('authenticate_user')()
I get an error of RecursionError: maximum recursion depth exceeded
.
And I have no idea how to call the method without giving me that error or some solution.
Is there a way to call such a method in the method __getattribute__
? Any solution?
The answer is yes, but very, very, very carefully...
The method
__getattribute__
together with its brother__setattr__
are perhaps two of the most complicated special methods to implement and in which more care must be taken.The problem with
__getattribute__
is almost always the same, we implement it, we think everything is perfect, we execute and boom! infinite recursion.The cause and its explanation are really very simple, but even knowing it perfectly, it is easy to make a mistake. What must always be marked on fire is:
It is therefore the entry point to resolve access to attributes through "dot notation", if it does not find the attribute it delegates to the other methods of the chain until it ends with
__getattr__
whether it is implemented.The cause of infinite recursion is whenever we call a method or access an attribute from it
__getattribute__
directly or indirectly. If__getattribute__
an attempt is made to access an attribute of the instance from within itselfself.atributo
, for example, when resolving ,self.atributo
it is calledself.__getattribute__("atributo")
, within which it attempts to accessself.atributo
, which again callsself.__getattribute__("atributo")
...The way to avoid it is to delegate to the method
__getattribute__
of a parent class the obtaining of the reference to the attribute/method, ultimately it will beobject.__getattribute__
if we do not inherit from anything. The normal thing is to usesuper()
for it, which returns a proxy object to the parent method that it touches, according to MRO, that we can use to call it.Case 1
Bad, very bad, it causes ( )
x = self.user
to be called again from itself...__getattribute__
self.__getattribute__("user")
Related question:
case 2
Well, by delegating to the parent method we avoid the recursive call.
case 3
No problem, as before, the resolution of the method is delegated to the parent
self.autentificate_user
and there is no recursive call.case 4
Again... We have only changed:
by:
but the error is very similar to case 1, only that in this case the access to an attribute from
__getattribute__
is not directly, it is the method thatsuper().__getattribute__
returns you that causes it to be called__getatributte__
again because ofreturn self.user
.You don't show the implementation of the method
authenticate_user
but I can say with some certainty that said method accesses attributes or methods of the instance at some point and therefore makes use of__getattribute__
.Case 5
case 6
case 7
Case No.
Depending on the specific implementation, there will be many possible solutions, but the idea of all of them is always the same: to avoid at all costs that anything you call or reference in
__getattribute__
end up at any time also callingself.__getattibute__
without there being a short circuit at some point .What about builtins and operators that use instance methods?
One might think that since it
str(self)
callsself.__str__
we would again have the problem that concerns us... Well, nostr
,implementado
in C, it does not directly reference the method using the implementation of__getattribute__
the instance, avoiding the problem. The same would happen withrepr
,sorted
,print
, operators like==
(__eq__
),!=
(__ne__
), etc. This is the reason why__str__
o__repr__
,__hash__
, etc should not be called directly, but through its builtins.That yes, it does not protect us from
__str__
accessing attributes that cause the recursive call in the implementation of the own, if in the previous example we change:by:
we roll it again.