Si has seguido hasta ahora la serie de artículos sobre descriptores, habrás visto que buena parte de la magia de los objetos en python se debe al método __getattribute__
que todo objeto adquiere de su antecesor común, la clase object
.
En el último artículo, donde hablaba de las optimizaciones de los métodos especiales, también comentaba algunas optimizaciones que tenían qué ver con el método __getattribute__
y proponía un ejercicio:
¿Sabrías qué es lo que pasa en el siguiente caso? ¿Se invoca el método getattribute en algún momento? ¿Sería una llamada implícita o explícita?
obj.__getattribute__("__getattribute__")
Quien se enfrenta a este código por primera vez, lo primero que piensa es que se va a producir una autorecursividad puesto que en el acceso al método __getattribute__
se debería invocar el propio método __getattribute__
y así indefinidamente.
Si embargo, cuando se prueba se ve que funciona tal y como se espera. Entonces, ¿cómo se evita la recursividad?
En el artículo de optimizaciones de los métodos especiales hablábamos de dos optimizaciones (atajos) de las llamadas implícitas a métodos especiales:
-
Implícitamente, sólo se buscará métodos especiales en la clase, nunca en el diccionario del objeto.
-
Implícitamente, nunca se accederá a un método especial a través de
__getattribute__
La intuición nos dice que aquí está la respuesta de que no tengamos autorecursividad.
Antes de analizar lo que está pasando, señalar que en el acceso a atributos se usa el operador ‘.
‘ (punto) que, como cualquier otro operador, está sujeto a las mismas optimizaciones que hemos apuntado. Para su labor, el operador .
empleará el método especial __getattribute__
.
La invocación obj.__getattribute__("atributo")
se produce en dos pasos:
- Implícitamente, el operador ‘
.
‘ accede directamente al método__getattribute__
, aplicando las optimizaciones. - Se invoca explícitamente a
__getattribute__
para que retorne el valor del"atributo"
Así pues, el resultado final consiste en la combinación de una llamada implícita y otra explícita.
Como corolario, se puede afirmar que “Nunca se invocará a __getattribute__
para acceder a __getattribute__
“. No será la primera vez que alguien lo haya intentado.