Декоратор @property позволяет превратить метод в "виртуальный" атрибут класса: он выглядит для пользователя класса как обычное свойство, но управляется логикой Python-функции. Изначально в Python все атрибуты экземпляра были доступны напрямую, но для поддержки инкапсуляции понадобился механизм управления доступом к данным без изменения интерфейса класса.
История:
В ранних версиях Python не было явного механизма для ограничения доступа к атрибутам. Задача инкапсуляции решалась через соглашения (например, подчеркивание), но любые изменения логики хранения/валидирования значений нарушали совместимость. С появлением декоратора @property появилась возможность объявить метод как атрибут, снабдить его геттером, сеттером и делитером, сохранив прежний интерфейс класса.
Проблема:
Когда требуется впоследствии добавить логику валидации или вычисления значения поля, переконструировать весь интерфейс слишком дорого — нужно менять все места доступа к переменной. При этом видимость внутренней реализации поля не должна быть раскрыта внешнему потребителю класса. @property позволяет легко "виртуализировать" доступ.
Решение:
@property реализует паттерн "геттер/сеттер с защитой интерфейса": можно добавить вычислимое свойство или контролировать установку значения без изменения клиентского кода.
Пример кода:
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Температура ниже абсолютного нуля!") self._celsius = value @property def fahrenheit(self): return self._celsius * 9 / 5 + 32 # Использование obj = Temperature(25) print(obj.celsius) # 25 print(obj.fahrenheit) # 77.0 obj.celsius = -300 # ValueError
Ключевые особенности:
Всегда ли можно добавить property к любому полю класса без нарушения обратной совместимости?
Нет. Если атрибут был публичным и его использовали как обычную переменную, замена на property возможна прозрачно. Но если где-то обращения шли через __dict__ или выполнялись проверки на тип через type(obj.attr), возможно неожиданное поведение.
Можно ли объявить только setter без геттера для свойства?
Нет, вначале обязателен геттер (метод с @property), иначе Python не знает, к какому свойству "привязать" setter.
Какой порядок у декораторов @property/@setter/@deleter и почему?
Всегда сначала пишется @property (он создает объект свойства), затем через имя метода — @<name>.setter и @<name>.deleter (они дополняют ранее созданное property).
class A: @property def value(self): ... @value.setter def value(self, v): ...
Поля класса были публичными, затем добавили вычислимое свойство через property после выпуска публичного API. В старом коде обращение к атрибуту неявно меняет поведение или вызывает ошибку.
Плюсы:
Минусы:
Сразу при проектировании поля были определены как закрытые (с одним подчеркиванием) и пользователи работали только через методы/properties. Добавление новой логики в property в будущем прошло без изменений клиентского интерфейса.
Плюсы:
Минусы: