Ответ
@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: Температура ниже абсолютного нуля!
Зачем это нужно:
- Инкапсуляция: Позволяет скрыть внутреннее представление данных (например,
_radius), предоставив контролируемый публичный интерфейс. - Валидация: Сеттер позволяет проверять и отклонять некорректные значения.
- Вычисляемые атрибуты: Можно создавать атрибуты, которые вычисляются на лету на основе других данных.
- Обратная совместимость: Если ранее у класса был публичный атрибут
x, и вы решили добавить логику при его изменении, вы можете превратить его в property, не ломая существующий код, который используетobj.x.
Ответ 18+ 🔞
Э, слушай, а вот этот @property — это вообще огонь, реально удобная штука. Представь, у тебя есть класс, и ты хочешь сделать так, чтобы к какому-то значению можно было обращаться, как к простому полю, но при этом, когда его читаешь или меняешь, срабатывала твоя хитрая логика. Вот для этого он и нужен, этакий волшебный плащ для твоих методов.
Как оно, блядь, устроено: По сути, этот декоратор создаёт 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 (сеттера-то нет, ебать копать!)
А вот тут уже полный контроль, с сеттером и всем таким:
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):
"""Ещё одно свойство, которое из celsius выводится."""
return self._celsius * 9/5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
"""Установка по Фаренгейту автоматом пересчитает Цельсии. Удобно, блядь!"""
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: Температура ниже абсолютного нуля! Вот тебе и валидация.
И зачем весь этот цирк, спросишь?
- Инкапсуляция, ёпта: Прячешь внутреннюю кухню (типа
_radius), а наружу выдаёшь красивый и безопасный фасад. - Валидация — наше всё: В сеттер можно впихнуть проверки, чтобы никакой распиздяй не засунул в твой объект какую-нибудь дичь.
- Атрибуты на лету: Можно делать поля, которые вычисляются только когда к ним обращаются. Лениво и эффективно.
- Обратная совместимость — царь: Был у тебя публичный атрибут
x. Решил добавить логику при его изменении. Без property пришлось бы менять весь код, который к нему обращается. А так — превратил метод в property, и весь старый код даже не заметит подмены, доверия ебать ноль у него будет. Хитрая жопа, да?