Comment la __getattribute__
méthode est-elle utilisée?
Il est appelé avant la recherche pointillée normale. Si cela augmente AttributeError
, alors nous suivons __getattr__
.
L'utilisation de cette méthode est plutôt rare. Il n'y a que deux définitions dans la bibliothèque standard:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
Meilleur entrainement
La méthode appropriée pour contrôler par programme l'accès à un seul attribut est d'utiliser property
. La classe D
doit être écrite comme suit (avec le setter et le deleter en option pour reproduire le comportement prévu apparent):
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'''
Et l'utilisation:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
Une propriété est un descripteur de données, c'est donc la première chose recherchée dans l'algorithme de recherche en pointillé normal.
Options pour __getattribute__
Vous avez plusieurs options si vous devez absolument implémenter la recherche pour chaque attribut via __getattribute__
.
- lever
AttributeError
, provoquant __getattr__
l'appel (si implémenté)
- en renvoyer quelque chose en
- en utilisant
super
pour appeler le parent (probablementobject
implémentation )
- appel
__getattr__
- implémenter votre propre algorithme de recherche en pointillé d'une manière ou d'une autre
Par exemple:
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
Ce que vous vouliez à l'origine.
Et cet exemple montre comment vous pourriez faire ce que vous vouliez à l'origine:
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)
Et se comportera comme ceci:
>>> 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
Revue de code
Votre code avec des commentaires. Vous avez une recherche en pointillé sur vous-même __getattribute__
. C'est pourquoi vous obtenez une erreur de récursivité. Vous pouvez vérifier si le nom est "__dict__"
et utiliser super
pour contourner le problème, mais cela ne couvre pas __slots__
. Je vais laisser cela comme un exercice au lecteur.
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