Атрибуты и ограничение доступа через переопределение операторов
Обращение к атрибутам __getattr__ и __setattr__
__getattr__ - получить ссылку на атрибут.
НЕ вызывается, если интерпретатор может найти атрибут в дереве наследования.
Вызывается, если пытается получить ссылку на неопределенный (несуществующий) атрибут. Атрибут вычисляется динамически.
Сделаем класс, который ведет себя так, как у его экземпляров есть атрибут age и нет других атрибутов.
>>> class empty:
... def __getattr__(self, attrname):
... if attrname == 'age':
... return 40
... else:
... raise AttributeError, attrname
...
>>> x = empty()
>>> x.age
40
>>> x.name
...текст сообщения об ошибке опущен...
AttributeError: name
__setattr__ - вызывается всегда, когда пишем self.atr = value
.
Вызывается self.__setattr__('atr', value)
Если внутри этого метода нужно сделать self.name = x
, то не пишите так (получите бесконечный цикл рекурсивных вызовов), а пишите self.__dict__['name'] = x
>>> class accesscontrol:
... def __setattr__(self, attr, value):
... if attr == 'age':
... self.__dict__[attr] = value
... else:
... raise AttributeError, attr + ' not allowed'
...
>>> x = accesscontrol()
>>> x.age = 40 # Вызовет метод __setattr__
>>> x.age
40
>>> x.name = 'mel'
...текст сообщения об ошибке опущен...
AttributeError: name not allowed
Как еще управлять атрибутами (см. позже):
- __getattribute__ - обращение к любым атрибутам (даже уже существующим). Пишем обращение к атрибуту так же, как в __setattr__, чтобы избежать рекурсии:
self.__dict__['name'] = x
- функция
property
() - дескрипторы связывают методы __get__ и __set__ с доступом к нужным атрибутам класса.
Пример: ограничиваем права доступа через __setattr__
class PrivateExc(Exception): pass # Создали пользовательское исключение
class Privacy:
def __setattr__(self, attrname, value): # Вызывается self.attrname = value
if attrname in self.privates:
raise PrivateExc(attrname, self)
else:
self.__dict__[attrname] = value # ибо self.attrname = value будет рекурсивно вызывать __setattr__
class Test1(Privacy):
privates = ['age']
class Test2(Privacy):
privates = ['name', 'pay']
def __init__(self):
self.__dict__['name'] = 'Tom'
x = Test1()
y = Test2()
x.name = 'Bob'
y.name = 'Sue' # <== ошибка
y.age = 30
x.age = 40 # <== ошибка
Полная реализация будет позже.