Come viene __getattribute__
utilizzato il metodo?
Viene chiamato prima della normale ricerca punteggiata. Se aumenta AttributeError
, allora chiamiamo __getattr__
.
L'uso di questo metodo è piuttosto raro. Ci sono solo due definizioni nella libreria standard:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
La migliore pratica
Il modo corretto per controllare a livello di codice l'accesso a un singolo attributo è con property
. La classe D
dovrebbe essere scritta come segue (con setter e deleter opzionalmente per replicare l'apparente comportamento previsto):
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'''
E utilizzo:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
Una proprietà è un descrittore di dati, quindi è la prima cosa da cercare nel normale algoritmo di ricerca puntata.
Opzioni per __getattribute__
Hai diverse opzioni se devi assolutamente implementare la ricerca di ogni attributo tramite __getattribute__
.
- rilanciare
AttributeError
, provocando la __getattr__
chiamata (se implementata)
- restituire qualcosa da esso entro
- usando
super
per chiamare l' object
implementazione genitore (probabilmente )
- chiamando
__getattr__
- implementando in qualche modo il tuo algoritmo di ricerca puntato
Per esempio:
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
Quello che volevi originariamente.
E questo esempio mostra come potresti fare ciò che volevi originariamente:
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)
E si comporterà così:
>>> 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
Revisione del codice
Il tuo codice con commenti. Hai una ricerca puntata su se stesso in __getattribute__
. Questo è il motivo per cui ottieni un errore di ricorsione. È possibile verificare se il nome è "__dict__"
e utilizzare super
per risolvere il problema, ma questo non copre __slots__
. Lo lascio come esercizio al lettore.
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