Estoy tratando de entender la diferencia __getattr__
y __getattribute__
, sin embargo, no lo estoy logrando.
La respuesta a la pregunta de desbordamiento de pila Diferencia entre __getattr__
vs__getattribute__
dice:
__getattribute__
se invoca antes de mirar los atributos reales en el objeto, por lo que puede ser complicado implementarlo correctamente. Puede terminar en infinitas recursiones muy fácilmente.
No tengo ni idea de lo que eso significa.
Luego continúa diciendo:
Casi seguro que quieres
__getattr__
.
¿Por qué?
Leí que si __getattribute__
falla, __getattr__
se llama. Entonces, ¿por qué hay dos métodos diferentes que hacen lo mismo? Si mi código implementa las nuevas clases de estilo, ¿qué debo usar?
Estoy buscando algunos ejemplos de código para aclarar esta pregunta. Busqué en Google lo mejor que pude, pero las respuestas que encontré no discuten el problema a fondo.
Si hay alguna documentación, estoy listo para leerla.
fuente
Respuestas:
Algunos conceptos básicos primero.
Con los objetos, debe lidiar con sus atributos. Normalmente lo hacemos
instance.attribute
. A veces necesitamos más control (cuando no sabemos el nombre del atributo de antemano).Por ejemplo,
instance.attribute
se convertiríagetattr(instance, attribute_name)
. Con este modelo, podemos obtener el atributo al proporcionar el nombre_atributo como una cadena.Uso de
__getattr__
También puede decirle a una clase cómo lidiar con los atributos que no administra explícitamente y hacerlo a través del
__getattr__
método.Python llamará a este método cada vez que solicite un atributo que aún no se haya definido, por lo que puede definir qué hacer con él.
Un caso de uso clásico:
Advertencias y uso de
__getattribute__
Si necesita capturar cada atributo independientemente de si existe o no , úselo
__getattribute__
en su lugar. La diferencia es que__getattr__
solo se solicitan atributos que en realidad no existen. Si configura un atributo directamente, haciendo referencia a ese atributo lo recuperará sin llamar__getattr__
.__getattribute__
Se llama todos los tiempos.fuente
getattr(instance, attribute_name)
". ¿No debería ser__getattribute__(instance, attribute_name)
?getattr
es una función incorporada.getattr(foo, 'bar')
es equivalente afoo.bar
.__getattr__
solo se llama si está definido, ya que no se hereda deobject
__getattribute__
se llama siempre que se produce un acceso de atributo.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 almacenadosself.__dict__
y disponibles por su nombre. La líneaintenta acceder a la
a
atributo def
. Esto requieref.__getattribute__('a')
.__getattribute__
Luego intenta cargarself.__dict__
.__dict__
es un atributo deself == f
Python y, porf.__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, entoncesf
tiene una
atributo.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__
essuper(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 actualself
y 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__
.Lo dejaré como ejercicio.
fuente
Creo que las otras respuestas han hecho un gran trabajo al explicar la diferencia entre
__getattr__
y__getattribute__
, pero una cosa que podría no estar clara es por qué querrías usarla__getattribute__
. Lo bueno de esto__getattribute__
es que esencialmente te permite sobrecargar el punto cuando accedes a una clase. Esto le permite personalizar cómo se accede a los atributos en un nivel bajo. Por ejemplo, supongamos que quiero definir una clase donde todos los métodos que solo toman un argumento propio se tratan como propiedades:Y del intérprete interactivo:
Por supuesto, este es un ejemplo tonto y probablemente nunca quieras hacer esto, pero te muestra el poder que puedes obtener al anular
__getattribute__
.fuente
He pasado por la excelente explicación de los demás. Sin embargo, encontré una respuesta simple de este blog Python Magic Methods y
__getattr__
. Todos los siguientes son de allí.Usando el
__getattr__
método mágico, podemos interceptar esa búsqueda de atributos inexistente y hacer algo para que no falle:Pero si el atributo existe,
__getattr__
no se invocará:__getattribute__
es similar a__getattr__
, con la diferencia importante que__getattribute__
interceptará CADA búsqueda de atributos, no importa si el atributo existe o no.En ese ejemplo, el
d
objeto ya tiene un valor de atributo. Pero cuando intentamos acceder a él, no obtenemos el valor esperado original ("Python"); solo estamos recibiendo lo que sea__getattribute__
devuelto. Significa que prácticamente hemos perdido el atributo de valor; se ha convertido en "inalcanzable".fuente