В Python можно скрыть атрибуты объекта, используя:
_attribute) или двух (__attribute) подчеркиваний. Одно подчеркивание указывает, что атрибут предназначен для внутреннего использования (protected), а два подчеркивания вызывают "name mangling" (делают атрибут более сложным для доступа извне, но не полностью недоступным).@property, @attribute.setter, @attribute.deleter позволяет контролировать доступ к атрибутам через методы.Пример (name mangling):
class MyClass:
    def __init__(self):
      self.__private_attribute = 10
  obj = MyClass()
  # print(obj.__private_attribute) # AttributeError
  print(obj._MyClass__private_attribute) # Работает, но не рекомендуетсяПример (properties):
class MyClass:
    def __init__(self):
      self._my_attribute = None
    @property
    def my_attribute(self):
      return self._my_attribute
    @my_attribute.setter
    def my_attribute(self, value):
      # Логика валидации или преобразования значения
      self._my_attribute = value
  В Python можно скрыть атрибуты объекта от внешнего доступа, используя несколько подходов:
Наиболее распространенный способ – использовать одинарное подчеркивание (_attribute) или двойное подчеркивание (__attribute) перед именем атрибута. Это соглашение сигнализирует разработчикам, что атрибут предназначен для внутреннего использования и не должен изменяться или использоваться напрямую извне класса.
_attribute:  Означает "protected" (защищенный). Это соглашение предполагает, что атрибут не должен использоваться вне класса и его подклассов, но Python не применяет никаких ограничений.__attribute: Означает "private" (приватный).  Python применяет механизм искажения имен (name mangling), чтобы сделать атрибут менее доступным извне.  Имя атрибута изменяется на _ClassName__attribute, где ClassName – имя класса. Это усложняет доступ, но не делает атрибут полностью недоступным.Пример:
class MyClass:
  def __init__(self):
    self._protected_attribute = 10
    self.__private_attribute = 20
  def get_private(self):
    return self.__private_attribute  # Доступ внутри класса
obj = MyClass()
print(obj._protected_attribute) # Можно получить доступ, но не рекомендуется
# print(obj.__private_attribute) # Вызовет AttributeError
print(obj.get_private()) # Правильный способ доступа к "приватному" атрибуту
print(obj._MyClass__private_attribute) # Очень не рекомендуется, но возможно
      Свойства предоставляют более контролируемый способ управления доступом к атрибутам.  Можно использовать декораторы @property, @attribute.setter, и @attribute.deleter, чтобы определить методы для получения, установки и удаления значения атрибута.  Это позволяет добавить логику валидации, преобразования или другую логику при доступе к атрибуту.
Пример:
class MyClass:
  def __init__(self):
    self._my_attribute = 0
  @property
  def my_attribute(self):
    return self._my_attribute
  @my_attribute.setter
  def my_attribute(self, value):
    if value < 0:
      raise ValueError("Значение не может быть отрицательным")
    self._my_attribute = value
  @my_attribute.deleter
  def my_attribute(self):
    del self._my_attribute
obj = MyClass()
print(obj.my_attribute) # Получение значения через getter
obj.my_attribute = 5 # Установка значения через setter
# obj.my_attribute = -1 # Вызовет ValueError
del obj.my_attribute # Удаление атрибута через deleter
      Важно: В Python нет абсолютной приватности в том смысле, как это реализовано в некоторых других языках. Всегда существует способ обойти механизмы сокрытия. Цель этих механизмов – поощрять хорошую практику программирования и предотвращать случайные ошибки, а не обеспечивать строгую безопасность.