Что такое @property в Python?

«Что такое @property в Python?» — вопрос из категории Python, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Как это работает: Декоратор создает property object, который использует методы-геттер, сеттер и делитер.

Базовый пример (вычисляемый атрибут):

class Circle:
    def __init__(self, radius):
        self._radius = radius  # Приватный атрибут

    @property
    def radius(self):
        """Геттер. Вызывается при обращении к circle.radius"""
        return self._radius

    @property
    def area(self):
        """Вычисляемое свойство. Площадь зависит от радиуса."""
        return 3.14159 * self._radius ** 2

# Использование
c = Circle(5)
print(c.radius)  # 5 -> Вызывается геттер radius
print(c.area)    # 78.53975 -> Вызывается геттер area
# c.area = 10  # AttributeError: can't set attribute (у area нет сеттера)

Полный контроль с сеттером и делитером:

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

    @fahrenheit.setter
    def fahrenheit(self, value):
        """Сеттер для свойства fahrenheit изменяет базовое значение celsius."""
        self._celsius = (value - 32) * 5/9

# Использование
t = Temperature(25)
print(t.fahrenheit)  # 77.0

t.fahrenheit = 100
print(t.celsius)     # 37.777... -> celsius изменилось через сеттер fahrenheit

# t.celsius = -300  # ValueError: Температура ниже абсолютного нуля!

Зачем это нужно:

  1. Инкапсуляция: Позволяет скрыть внутреннее представление данных (например, _radius), предоставив контролируемый публичный интерфейс.
  2. Валидация: Сеттер позволяет проверять и отклонять некорректные значения.
  3. Вычисляемые атрибуты: Можно создавать атрибуты, которые вычисляются на лету на основе других данных.
  4. Обратная совместимость: Если ранее у класса был публичный атрибут x, и вы решили добавить логику при его изменении, вы можете превратить его в property, не ломая существующий код, который использует obj.x.