¿Cómo se __getattribute__
usa el método?
Se llama antes de la búsqueda de puntos normal. Si sube AttributeError
, llamamos__getattr__
.
El uso de este método es bastante raro. Solo hay dos definiciones en la biblioteca estándar:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
Mejores prácticas
La forma correcta de controlar mediante programación el acceso a un solo atributo es con property
. La clase D
debe escribirse de la siguiente manera (con el establecedor y el eliminador opcionalmente para replicar el comportamiento previsto aparente):
class D(object):
def __init__(self):
self.test2=21
@property
def test(self):
return 0.
@test.setter
def test(self, value):
'''dummy function to avoid AttributeError on setting property'''
@test.deleter
def test(self):
'''dummy function to avoid AttributeError on deleting property'''
Y uso:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
Una propiedad es un descriptor de datos, por lo que es lo primero que se busca en el algoritmo de búsqueda de puntos normal.
Opciones para __getattribute__
Tiene varias opciones si es absolutamente necesario implementar la búsqueda para cada atributo a través de __getattribute__
.
- subir
AttributeError
, haciendo __getattr__
que se llame (si se implementa)
- devolver algo de él por
- usando
super
para llamar a la object
implementación de los padres (probablemente )
- vocación
__getattr__
- implementar su propio algoritmo de búsqueda de puntos de alguna manera
Por ejemplo:
class NoisyAttributes(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self, name):
print('getting: ' + name)
try:
return super(NoisyAttributes, self).__getattribute__(name)
except AttributeError:
print('oh no, AttributeError caught and reraising')
raise
def __getattr__(self, name):
"""Called if __getattribute__ raises AttributeError"""
return 'close but no ' + name
>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20
Lo que originalmente querías.
Y este ejemplo muestra cómo podría hacer lo que originalmente quería:
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name=='test':
return 0.
else:
return super(D, self).__getattribute__(name)
Y se comportará así:
>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test
Traceback (most recent call last):
File "<pyshell#216>", line 1, in <module>
del o.test
AttributeError: test
Revisión de código
Tu código con comentarios. Tiene una búsqueda punteada de sí mismo en __getattribute__
. Es por eso que obtiene un error de recursividad. Puede verificar si el nombre es "__dict__"
y usar super
para una solución alternativa, pero eso no cubre __slots__
. Dejaré eso como un ejercicio para el lector.
class D(object):
def __init__(self):
self.test=20
self.test2=21
def __getattribute__(self,name):
if name=='test':
return 0.
else: # v--- Dotted lookup on self in __getattribute__
return self.__dict__[name]
>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp