__getattribute__ se llama siempre que se produce un acceso de atributo.
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
Esto causará una recursión infinita. El culpable aquí es la línea return self.__dict__[attr]. Supongamos (está lo suficientemente cerca de la verdad) que todos los atributos están almacenados self.__dict__y disponibles por su nombre. La línea
f.a
intenta acceder a la a atributo de f. Esto requiere f.__getattribute__('a'). __getattribute__Luego intenta cargar self.__dict__. __dict__es un atributo de self == fPython y, por f.__getattribute__('__dict__')lo tanto, vuelve a intentar acceder al atributo '__dict__'. Esta es la recursión infinita.
Si se __getattr__hubiera utilizado en su lugar, entonces
- Nunca se habría ejecutado porque
ftiene un aatributo.
- Si se hubiera ejecutado, (digamos que solicitó
f.b ), entonces no habría sido llamado para buscar __dict__porque ya está allí y __getattr__se invoca solo si todos los demás métodos para encontrar el atributo han fallado .
La forma 'correcta' de escribir la clase anterior usando __getattribute__es
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)vincula el __getattribute__método de la superclase 'más cercana' (formalmente, la siguiente clase en el Orden de resolución de método de la clase, o MRO) al objeto actual selfy luego lo llama y deja que eso haga el trabajo.
Todos estos problemas se evitan mediante el uso de __getattr__ que permite a Python hacer lo normal hasta que no se encuentre un atributo. En ese punto, Python le da el control a su __getattr__método y le permite encontrar algo.
También vale la pena señalar que puede encontrarse con una recursión infinita con __getattr__ .
class Foo(object):
def __getattr__(self, attr):
return self.attr
Lo dejaré como ejercicio.