Использовать декоратор @property.
class MyClass:
    def __init__(self, value):
      self._value = value
    @property
    def value(self):
      return self._value
    @value.setter
    def value(self, new_value):
      self._value = new_value
  Или использовать функцию property().
class MyClass:
    def __init__(self, value):
      self._value = value
    def get_value(self):
      return self._value
    def set_value(self, new_value):
      self._value = new_value
    value = property(get_value, set_value)
  Есть несколько способов сделать атрибут объекта доступным как свойство в Python. Основные подходы включают использование декораторов @property, @property.setter, @property.deleter, а также дескрипторов.
1. Использование декоратора @property:
Это самый распространенный и питонический способ. Он позволяет определить метод для чтения значения атрибута и представить его как свойство объекта.
class Circle:
  def __init__(self, radius):
    self._radius = radius  # Используем защищенный атрибут, т.к. можем контролировать доступ
  @property
  def radius(self):
    """Возвращает радиус круга."""
    return self._radius
  @radius.setter
  def radius(self, value):
    """Устанавливает радиус круга."""
    if value < 0:
      raise ValueError("Радиус не может быть отрицательным")
    self._radius = value
  @radius.deleter
  def radius(self):
    """Удаляет радиус круга."""
    del self._radius
# Пример использования
circle = Circle(5)
print(circle.radius)  # Вывод: 5
circle.radius = 10
print(circle.radius)  # Вывод: 10
try:
  circle.radius = -1
except ValueError as e:
  print(e) # Вывод: Радиус не может быть отрицательным
del circle.radius
try:
  print(circle.radius)
except AttributeError as e:
  print(e) # Вывод: 'Circle' object has no attribute '_radius'
В этом примере @property делает метод radius() доступным как свойство circle.radius. @radius.setter позволяет установить значение, а @radius.deleter позволяет удалить атрибут. Заметьте использование _radius как защищенного атрибута, который хранит фактическое значение.
2. Использование дескрипторов:
Дескрипторы - это более мощный механизм, который позволяет контролировать доступ к атрибутам на более низком уровне. Они реализуют методы __get__, __set__ и __delete__.
class ValidRadius:
    def __init__(self):
      self._radius = None
    def __get__(self, instance, owner):
      return self._radius
    def __set__(self, instance, value):
      if value < 0:
        raise ValueError("Радиус не может быть отрицательным")
      self._radius = value
    def __delete__(self, instance):
      del self._radius
class Circle:
  radius = ValidRadius()
  def __init__(self, radius):
    self.radius = radius
# Пример использования
circle = Circle(5)
print(circle.radius)  # Вывод: 5
circle.radius = 10
print(circle.radius)  # Вывод: 10
try:
  circle.radius = -1
except ValueError as e:
  print(e)  # Вывод: Радиус не может быть отрицательным
del circle.radius
try:
  print(circle.radius)
except AttributeError as e:
  print(e) # Вывод: 'ValidRadius' object has no attribute '_radius'
Здесь ValidRadius - это дескриптор.  Когда мы обращаемся к circle.radius, вызывается метод __get__ дескриптора. Когда мы присваиваем значение circle.radius = value, вызывается метод __set__ дескриптора.  Когда мы вызываем del circle.radius, вызывается метод __delete__ дескриптора.
Какой подход выбрать?
@property обычно является более предпочтительным, так как он более читабельный и простой в использовании.В любом случае, оба подхода позволяют сделать атрибуты объекта доступными как свойства, предоставляя возможность инкапсуляции и контроля доступа к данным.