Jak jest __getattribute__
używana metoda?
Jest wywoływana przed zwykłym wyszukiwaniem kropkowanym. Jeśli wzrośnie AttributeError
, dzwonimy__getattr__
.
Stosowanie tej metody jest raczej rzadkie. W bibliotece standardowej są tylko dwie definicje:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
Najlepsze praktyki
Właściwym sposobem programowego kontrolowania dostępu do pojedynczego atrybutu jest property
. Klasa D
powinna być zapisana w następujący sposób (z ustawieniem i usunięciem opcjonalnie w celu odtworzenia widocznego zamierzonego zachowania):
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'''
I użycie:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
Właściwość jest deskryptorem danych, dlatego jest pierwszą rzeczą, której szuka się w normalnym algorytmie wyszukiwania kropkowego.
Opcje dla __getattribute__
Masz kilka opcji, jeśli absolutnie musisz zaimplementować wyszukiwanie dla każdego atrybutu za pośrednictwem __getattribute__
.
- podnieść
AttributeError
, powodując __getattr__
wywołanie (jeśli zaimplementowano)
- zwrócić coś z tego przez
- za pomocą
super
wywołania nadrzędnej (prawdopodobnie object
) implementacji
- powołanie
__getattr__
- zaimplementowanie w jakiś sposób własnego algorytmu wyszukiwania kropkowego
Na przykład:
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
To, czego pierwotnie chciałeś.
Ten przykład pokazuje, jak możesz zrobić to, czego pierwotnie chciałeś:
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)
I będzie się tak zachowywać:
>>> 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
Przegląd kodu
Twój kod z komentarzami. Masz wykropkowane wyszukiwanie siebie w __getattribute__
. Dlatego pojawia się błąd rekursji. Możesz sprawdzić, czy nazwa jest "__dict__"
i użyć super
do obejścia, ale to nie obejmuje __slots__
. Zostawię to jako ćwiczenie dla czytelnika.
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