privados - ¿Cómo funciona el proceso de búsqueda de atributos python?
self python 3 (1)
Si agregó print("Metaclass getattribute invoked:", self, name)
, verá:
>>> c.__getattribute__
Class getattribute invoked: <__main__.C object at 0x2acdbb1430d0> __getattribute__
Metaclass getattribute invoked: <class ''__main__.C''> __name__
<bound method C.__getattribute__ of <__main__.C object at 0x2acdbb1430d0>>
La metaclase __getattribute__
se invoca para construir la repr
de la expresión c.__getattribute__
, para que pueda imprimir C
''s __name__
.
btw, __getattribute__
funciona igual para clases y metaclases; el atributo se busca primero en la instancia y luego en el tipo de instancia.
>>> Meta.foo = 1
>>> C.foo
(''Metaclass getattribute invoked:'', <class ''__main__.C''>, ''foo'')
1
>>> c.foo
(''Class getattribute invoked:'', <__main__.C object at 0x2acdbb1430d0>, ''foo'')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", 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
Cuando digo "proceso de búsqueda de atributos de python" quiero decir: ¿qué hace python cuando escribes x.foo?
Al buscar en la web no encontré demasiados documentos sobre esto, uno de los mejores documentos que encontré retomó el proceso de los siguientes pasos (puede ver el artículo completo aquí )
- Si attrname es un atributo especial (es decir, proporcionado por Python) para objectname, devuélvalo.
- Verifique el nombre de objeto .__ clase __.__ dict__ para attrname. Si existe y es un descriptor de datos, devuelva el resultado del descriptor. Buscar todas las bases de objectname .__ class__ para el mismo caso.
- Verifique el nombre de objeto .__ dict__ para attrname, y devuelva si lo encuentra. Si objectname es una clase, busca sus bases también. Si es una clase y existe un descriptor en ella o sus bases, devuelva el resultado del descriptor.
- Verifique el nombre de objeto .__ clase __.__ 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 hubiéramos regresado en el punto 2. Buscar todas las bases de objectname .__ class__ para el mismo caso.
- Levanta 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 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 compruebe las siguientes líneas con el resultado correspondiente:
>> C.__new__
Metaclass getattribute invoked: <class ''__main__.C''>
<built-in method __new__ of type object at 0x1E1B80B0>
>> C.__getattribute__
Metaclass getattribute invoked: <class ''__main__.C''>
<function __getattribute__ at 0x01457F18>
>> C.xyz
Metaclass getattribute invoked: <class ''__main__.C''>
Metaclass getattr invoked: xyz
None
>> c.__new__
Class getattribute invoked: (<__main__.C object at 0x013E7550>, ''__new__'')
<built-in method __new__ of type object at 0x1E1B80B0>
>> c.__getattribute__
Class getattribute invoked: (<__main__.C object at 0x01438DB0>, ''__getattribute__'')
Metaclass getattribute invoked: <class ''__main__.C''>
<bound method C.__getattribute__ of <__main__.C object at 0x01438DB0>>
>>
Las conclusiones que he tenido son (considerando que estamos buscando x.foo):
- __getattribute__ es diferente para las instancias de <type ''type''> y <type ''object''>. Para C.foo (), ''foo'' se busca primero en C .__ dict__ y se devuelve si se encuentra (en lugar del tipo de búsqueda (C)) y para x.foo () ''foo'' se busca en 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 es 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 documentación sobre esto, gracias.