Какие механизмы инкапсуляции существуют в Python?

Ответ

В Python инкапсуляция — это в первую очередь соглашение, а не строгое правило, как в Java или C++. Она реализуется с помощью следующих механизмов:

  1. Соглашения об именовании (префиксы _ и __):

    • Одинарное подчеркивание (_): Сигнализирует, что атрибут или метод является защищенным (protected) и предназначен для внутреннего использования в классе или его наследниках. Технически доступ к нему не ограничен.
      class MyClass:
        def __init__(self):
            self._internal_var = 10
    • Двойное подчеркивание (__): Делает атрибут или метод приватным (private) с помощью механизма Name Mangling. Python изменяет имя атрибута, добавляя к нему имя класса (_ClassName__attribute), что затрудняет случайный доступ извне.

      class MyClass:
        def __init__(self):
            self.__private_var = 20
      
      obj = MyClass()
      # print(obj.__private_var)  # AttributeError
      print(obj._MyClass__private_var) # Так доступ возможен
  2. Свойства (@property): Декоратор @property позволяет управлять доступом к атрибутам класса, превращая методы в атрибуты, доступные только для чтения. С помощью декораторов @<name>.setter и @<name>.deleter можно добавить логику для записи и удаления, например, для валидации данных.

    Пример с валидацией:

    class BankAccount:
        def __init__(self, initial_balance):
            if initial_balance < 0:
                raise ValueError("Начальный баланс не может быть отрицательным")
            self.__balance = initial_balance
    
        @property
        def balance(self):
            """Геттер для получения баланса."""
            return self.__balance
    
        @balance.setter
        def balance(self, value):
            """Сеттер с валидацией для изменения баланса."""
            if value < 0:
                raise ValueError("Баланс не может быть отрицательным")
            self.__balance = value
    
    account = BankAccount(100)
    print(account.balance)  # 100 (работает геттер)
    account.balance = 200     # OK (работает сеттер)
    # account.balance = -50   # Вызовет ValueError

    Такой подход позволяет скрыть внутреннюю реализацию (__balance) и предоставить безопасный публичный интерфейс (balance).

Ответ 18+ 🔞

Ну и вот, смотри, как у нас в Питоне с этой вашей инкапсуляцией дела обстоят. Не то чтобы её совсем нет, но она тут, блядь, больше на честном слове держится, а не на железобетонных правилах, как у этих зануд из Java. Тут всё через договорённости и хитрые уловки.

Первое — это договорняки по именам, с подчёркиваниями.
Один знак (_) — это типа намёк, мол, чувак, не лезь сюда, это для внутренней кухни. Но технически-то ты можешь, конечно, сука, залезть. Просто все понимают, что ты мудак, если полез.

class MyClass:
    def __init__(self):
        self._internal_var = 10  # Ну типа не трогай, а то в глаз получишь

А вот два знака (__) — это уже посерьёзнее, это Name Mangling, ёпта! Питон такой: "Ах, ты хочешь приватности? Щас я тебе имя так исковеркаю, что только мать родная узнает!" Он прилепляет спереди имя класса, и получается _MyClass__private_var. Снаружи просто так уже не достучишься, хотя, если очень хочется, то можно, блядь, всё.

class MyClass:
    def __init__(self):
        self.__private_var = 20  # Попробуй, сука, достань теперь!

obj = MyClass()
# print(obj.__private_var)  # Ошибка, нихуя не найдёт
print(obj._MyClass__private_var) # А вот так — пожалуйста, на тебе! Хитрый жополиз.

Второе — это свойства, @property, вообще магия, блядь!
С их помощью можно прикинуться просто атрибутом, а на самом деле внутри будет целая опера с геттерами и сеттерами. Идеально, чтобы спрятать свою жопу и не давать кому попало менять твои внутренние переменные.

Вот смотри, пример с банковским счётом, а то эти пользователи вечно норовят баланс в минус увести, пидарасы!

class BankAccount:
    def __init__(self, initial_balance):
        if initial_balance < 0:
            raise ValueError("Начальный баланс не может быть отрицательным, ты чё, охренел?")
        self.__balance = initial_balance  # Спрятано, как золото в сейфе

    @property
    def balance(self):
        """Геттер. Посмотреть можно, а тронуть — нет."""
        return self.__balance

    @balance.setter
    def balance(self, value):
        """Сеттер. Тут мы, блядь, проверяем, не долбоёб ли ты."""
        if value < 0:
            raise ValueError("Баланс не может быть отрицательным, ебаный рот!")
        self.__balance = value

account = BankAccount(100)
print(account.balance)  # 100 — работает геттер, всё ок
account.balance = 200   # 200 — сеттер пропустил, молодец
# account.balance = -50 # А вот тут, сука, получишь ValueError прямо в ебальник!

Вот так и живём: снаружи красивый интерфейс, а внутри — вся логика, валидация и крики "чё блядь?!" на неправильные данные. Инкапсуляция по-питонячьи, ёпта!