¿Cómo funciona el proceso de búsqueda de atributos de Python?

Cuando digo “proceso de búsqueda de atributo de Python” quiero decir: ¿qué hace python cuando escribes x.foo?

Buscando en la web no encontré demasiados documentos sobre esto, uno de los mejores artículos que encontré resumió el proceso a los siguientes pasos (puede ver el artículo completo aquí )

  1. Si attrname es un atributo especial (es decir, proporcionado por Python) para objectname, devuélvalo.
  2. Verifique el nombre de objeto .__ class __.__ dict__ para attrname. Si existe y es un descriptor de datos, devuelva el resultado del descriptor. Busque en todas las bases de objectname .__ class__ para el mismo caso.
  3. Verifique el nombre de objeto .__ dict__ para attrname, y devuelva si se encuentra. Si el nombre de objeto es una clase, también busque sus bases. Si es una clase y existe un descriptor en ella o en sus bases, devuelva el resultado del descriptor.
  4. Verifique el nombre de objeto .__ class __.__ dict__ para attrname. Si existe y no es un descriptor de datos, devuelva el resultado del descriptor. Si existe, y no es un descriptor, simplemente devuélvalo. Si existe y es un descriptor de datos, no deberíamos estar aquí porque habríamos regresado al punto 2. Buscar todas las bases de objectname .__ class__ para el mismo caso.
  5. Aumentar AttributeError.

Al principio, esto puede parecer correcto, pero el proceso de búsqueda de atributos es un poco más complicado, por ejemplo para x.foo, no se comporta igual si x es una clase o una instancia.

He encontrado algunas muestras que no se pueden explicar de esta manera. Considere el siguiente código de Python:

class Meta(type): def __getattribute__(self, name): print("Metaclass getattribute invoked:", self) return type.__getattribute__(self, name) def __getattr__(self, item): print('Metaclass getattr invoked: ', item) return None class C(object, metaclass=Meta): def __getattribute__(self, name): print("Class getattribute invoked:", args) return object.__getattribute__(self, name) c=C() 

Ahora revisa las siguientes líneas con la salida correspondiente:

 >> C.__new__ Metaclass getattribute invoked:   >> C.__getattribute__ Metaclass getattribute invoked:   >> C.xyz Metaclass getattribute invoked:  Metaclass getattr invoked: xyz None >> c.__new__ Class getattribute invoked: (, '__new__')  >> c.__getattribute__ Class getattribute invoked: (, '__getattribute__') Metaclass getattribute invoked:  <bound method C.__getattribute__ of > >> 

Las conclusiones que he obtenido son (considerando que estamos buscando x.foo):

  • __getattribute__ es diferente para las instancias de y . Para C.foo (), primero se busca ‘foo’ en C .__ dict__ y se devuelve si se encuentra (en lugar de buscar el tipo (C)) y para x.foo () se busca en ‘foo’ en el tipo (x) .__ dict__ y en x .__ dict__.
  • El método __getattribute__ siempre se resuelve en el tipo (x), lo que no entiendo aquí es el último caso: c .__ getattribute__, no el objeto contiene un método __getattribute__ (y C hereda del objeto), entonces, ¿por qué se obtiene el método metaclass getattribute? llamado.

¿¿Puede alguien explicar esto, por favor?? o al menos dime dónde puedo encontrar algo de documentación sobre esto, gracias.

Si agregó print("Metaclass getattribute invoked:", self, name) vería:

 >>> c.__getattribute__ Class getattribute invoked: <__main__.C object at 0x2acdbb1430d0> __getattribute__ Metaclass getattribute invoked:  __name__ > 

La metaclase __getattribute__ se invoca para construir la repr de la expresión c.__getattribute__ , para que pueda imprimir el __name__ C

btw, __getattribute__ funciona igual para las clases y las metaclases; el atributo se busca primero en la instancia y luego en el tipo de la instancia.

 >>> Meta.foo = 1 >>> C.foo ('Metaclass getattribute invoked:', , 'foo') 1 >>> c.foo ('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'foo') Traceback (most recent call last): File "", line 1, in  File "", line 5, in __getattribute__ AttributeError: 'C' object has no attribute 'foo' >>> C.bar = 2 >>> c.bar ('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'bar') 2