Як застосовується __getattribute__
метод?
Він викликається перед звичайним пунктирним пошуком. Якщо вона піднімається AttributeError
, то ми дзвонимо __getattr__
.
Використання цього методу досить рідкісне. У стандартній бібліотеці є лише два визначення:
$ grep -Erl "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py
Найкраща практика
Правильний спосіб програмного керування доступом до одного атрибуту є за допомогою property
. Клас D
повинен бути записаний так (за допомогою сеттера та делетера, необов'язково, для копіювання очевидного наміченого поведінки):
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'''
І використання:
>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
Властивість - це дескриптор даних, тому це перше, що потрібно шукати в звичайному алгоритмі крапки пошуку.
Параметри для __getattribute__
У вас є кілька варіантів, якщо вам абсолютно потрібно здійснити пошук для кожного атрибуту через __getattribute__
.
- підвищувати
AttributeError
, викликаючи __getattr__
виклик (якщо він реалізований)
- повернути щось з нього
- використовуючи
super
для виклику батьківської (можливо object
,) реалізації
- дзвінок
__getattr__
- реалізувати свій власний алгоритм крапкового пошуку
Наприклад:
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
Те, що ви спочатку хотіли.
І цей приклад показує, як ви могли робити те, що спочатку хотіли:
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)
І поведе себе так:
>>> 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
Перегляд коду
Ваш код із коментарями. У вас є пунктирний пошук самостійно __getattribute__
. Ось чому ви отримуєте помилку рекурсії. Ви можете перевірити, чи є ім'я "__dict__"
та використовувати його super
для вирішення, але це не стосується __slots__
. Я залишу це як вправу для читача.
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