property: вычисляемые атрибуты
@property позволяет вызывать метод так, будто это обычный атрибут — с проверкой при присваивании и без изменения синтаксиса снаружи.
property: проверка при записи, без изменения синтаксиса чтения
Декоратор @property позволяет обращаться к методу ТАК ЖЕ,
как к обычному атрибуту — без круглых скобок:
class Krug:
def __init__(self, radius):
self._radius = radius
@property
def ploshchad(self):
return 3.14159 * self._radius ** 2
krug = Krug(10)
print(krug.ploshchad) # 314.159 — без скобок, хотя это метод!
ploshchad нигде не хранится — она вычисляется заново при
каждом обращении. Снаружи это неотличимо от обычного атрибута.
@x.setter: проверка при присваивании
class Krug:
def __init__(self, radius):
self.radius = radius # проходит через setter ниже
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("радиус должен быть положительным")
self._radius = value
krug = Krug(10)
krug.radius = 5 # проходит проверку
krug.radius = -1 # ValueError — присваивание отклонено
radius — обычный атрибут, а завтра понадобилась проверка — можно превратить его в property, и весь код, который писал krug.radius = 5, продолжит работать без единой правки. Именно поэтому в Python принято начинать с обычных атрибутов и добавлять property только когда проверка действительно понадобилась — а не оборачивать в property вообще всё с самого начала.class Krug:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
def radius(self, value): # забыли @radius.setter сверху!
self._radius = value
krug = Krug(10)
krug.radius = 5
>>> krug.radius = 5
>>> print(krug.radius)
5
Ошибки не будет — и это как раз самое коварное. Без декоратора @radius.setter вторая функция radius — это отдельный обычный метод, который просто ЗАМЕНИЛ собой property сверху (то же имя в теле класса!). После этого Krug.radius — уже не property, а обычная функция, и krug.radius = 5 просто создаёт атрибут ЭКЗЕМПЛЯРА radius, молча затеняя и метод, и всю проверку из §14.11 — печатается число 5, а не результат getter'а.
@radius.setter
def radius(self, value):
self._radius = value