Python 3 __getattribute__ vs comportamiento de acceso a puntos

Leí un poco en la búsqueda de atributos de objetos de python (aquí: https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/#object-attribute-lookup ).

Parece bastante sencillo, así que lo probé (python3):

class A: def __getattr__(self, attr): return (1,2,3) a = A() a.foobar #returns (1,2,3) as expected a.__getattribute__('foobar') # raises AttributeError 

Mi pregunta es: ¿no se supone que los dos son idénticos?

¿Por qué el segundo genera un error de atributo?

Entonces, aparentemente, la respuesta es que la lógica para a.foobar ES diferente de la lógica para a.__getattribute("foobar") . Según el modelo de datos : a.foobar llama a.__getattribute("foobar") y, si genera un AttributeError, llama a.-__getattr__('foobar')

Por lo que parece que el artículo tiene un error en su diagtwig. ¿Es esto correcto?

Y otra pregunta: ¿Dónde se encuentra la lógica real para un.foobar? Pensé que estaba en __getattribute__ pero aparentemente no del todo.

Editar: No es un duplicado de

Diferencia entre __getattr__ vs __getattribute__ . Estoy preguntando aquí cuál es la diferencia entre object.foo y object.__getattribute__("foo") . Esto es diferente de __getattr__ vs __getatribute__ que es trivial …

Es fácil tener la impresión de que __getattribute__ es responsable de más de lo que realmente es. thing.attr no se traduce directamente a thing.__getattribute__('attr') , y __getattribute__ no es responsable de llamar a __getattr__ .

El __getattr__ a __getattr__ ocurre en la parte de la maquinaria de acceso de atributo que se encuentra fuera de __getattribute__ . El proceso de búsqueda de atributos funciona así:

  • Encuentre el método __getattribute__ través de una búsqueda directa de la MRO del tipo de objeto, sin pasar por el proceso de búsqueda de atributos regular.
  • Pruebe __getattribute__ .
    • Si __getattribute__ devolvió algo, el proceso de búsqueda de atributos está completo y ese es el valor del atributo.
    • Si __getattribute__ generó un __getattribute__ de Atributo, el proceso de búsqueda de atributos se completa y la excepción se propaga fuera de la búsqueda.
    • De lo contrario, __getattribute__ generó un AttributeError. La búsqueda continúa.
  • Encuentre el método __getattr__ la misma manera que encontramos __getattribute__ .
    • Si no hay __getattr__ , se completa el proceso de búsqueda de atributos y se propaga el atributo AttributeError de __getattribute__ .
  • Pruebe __getattr__ , y devuelva o aumente lo que __getattr__ devuelva o __getattr__ .

Al menos, en términos de la semántica del lenguaje, funciona así. En términos de la implementación de bajo nivel, algunos de estos pasos pueden optimizarse en los casos en que son innecesarios, y hay ganchos en C como tp_getattro que no he descrito. No tiene que preocuparse por ese tipo de cosas a menos que quiera sumergirse en el código fuente del intérprete CPython.