Глава 14 · Создаём объекты реального мира

property: вычисляемые атрибуты

@property позволяет вызывать метод так, будто это обычный атрибут — с проверкой при присваивании и без изменения синтаксиса снаружи.

property: проверка при записи, без изменения синтаксиса чтения

Декоратор @property позволяет обращаться к методу ТАК ЖЕ, как к обычному атрибуту — без круглых скобок:

property_getter.py
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: проверка при присваивании

property_setter.py
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 — присваивание отклонено
Главная польза property: код снаружи не меняется
Если сегодня radius — обычный атрибут, а завтра понадобилась проверка — можно превратить его в property, и весь код, который писал krug.radius = 5, продолжит работать без единой правки. Именно поэтому в Python принято начинать с обычных атрибутов и добавлять property только когда проверка действительно понадобилась — а не оборачивать в property вообще всё с самого начала.
Debug Lab 6: забыли декоратор @radius.setter
bez_setter.py
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'а.

Исправленный код
s_setter.py
    @radius.setter
    def radius(self, value):
        self._radius = value
Практика: property
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки
Открыть практику →