…·········print('inside the getter')
…·········return self.hidden_name
…·····@name.setter
…·····def name(self, input_name):
…·········print('inside the setter')
…·········self.hidden_name = input_name
Вы все еще можете получать доступ к атрибуту name, но в этом случае не существует видимых методов get_name() или set_name():
>>> fowl = Duck('Howard')
>>> fowl.name
inside the getter
'Howard'
>>> fowl.name = 'Donald'
inside the setter
>>> fowl.name
inside the getter
'Donald'
Если кто-то догадается, что мы называли наш атрибут hidden_name, он сможет считать и записать его непосредственно с помощью конструкции fowl.hidden_name. В следующем разделе вы увидите особый способ именования закрытых атрибутов в Python.
В обоих предыдущих примерах мы использовали свойство name, чтобы обратиться к отдельному атрибуту (в нашем случае hidden_name), который хранится внутри объекта. Свойство может ссылаться и на
>>> class Circle():
…·····def __init__(self, radius):
…·········self.radius = radius
…·····@property
…·····def diameter(self):
…·········return 2 * self.radius
…
Мы создаем объект класса Circle, задав значение его атрибута radius:
>>> c = Circle(5)
>>> c.radius
5
Мы можем обратиться к свойству diameter точно так же, как к атрибуту вроде radius:
>>> c.diameter
10
А вот и самое интересное — мы можем изменить значение атрибута radius в любой момент и свойство diameter будет рассчитано на основе текущего значения атрибута radius:
>>> c.radius = 7
>>> c.diameter
14
Если вы не укажете сеттер для атрибута, то не сможете устанавливать его значение извне. Это удобно для атрибутов, которые должны быть доступны только для чтения:
>>> c.diameter = 20
Traceback (most recent call last):
··File "
AttributeError: can't set attribute
У использования свойств вместо непосредственного доступа к атрибутам имеется еще одно преимущество: если вы измените определение атрибута, вам нужно будет поправить только код внутри определения класса вместо того, чтобы править все вызовы.
Искажение имен для безопасности
В примере с классом Duck из предыдущего раздела мы вызывали наш (не полностью) скрытый атрибут hidden_name. Python предлагает соглашения по именованию для атрибутов, которые не должны быть видимы за пределами определения их классов: имена начинаются с двух нижних подчеркиваний (__).
Переименуем атрибут hidden_name в __name, как показано здесь:
>>> class Duck():
…·····def __init__(self, input_name):
…·········self.__name = input_name
…·····@property
…·····def name(self):
…·········print('inside the getter')
…·········return self.__name
…·····@name.setter
…·····def name(self, input_name):
…·········print('inside the setter')
…·········self.__name = input_name
…
Теперь проверим, работает ли все как полагается:
>>> fowl = Duck('Howard')
>>> fowl.name
inside the getter
'Howard'
>>> fowl.name = 'Donald'
inside the setter
>>> fowl.name
inside the getter
'Donald'
Выглядит хорошо. И вы не можете получить доступ к атрибуту __name:
>>> fowl.__name
Traceback (most recent call last):
··File "
AttributeError: 'Duck' object has no attribute '__name'
Это соглашение по именованию не делает атрибут закрытым, но Python искажает имя для того, чтобы внешний код не наткнулся на него. Если вам любопытно и вы никому не расскажете, я покажу вам, как будет выглядеть атрибут:
>>> fowl._Duck__name
'Donald'