Инкапсуляция и приватные атрибуты в Python

В этом руководстве вы узнаете об инкапсуляции и о том, как использовать приватные атрибуты для выполнения инкапсуляции в Python.

Содержание

Что такое инкапсуляция в Python?

Существует четыре фундаментальные концепции объектно-ориентированного программирования: абстракция, инкапсуляция, наследование и полиморфизм.

Инкапсуляция в Python— это пакет данных и функций, которые работают с этими данными, в одном объекте. Тем самым вы сможете скрыть внутреннее состояние объекта снаружи. Это известно как сокрытие информации.

Класс является примером инкапсуляции. Класс объединяет данные и методы в один блок и предоставляет доступ к своим атрибутам через методы.

Идея сокрытия информации заключается в том, что если у вас есть атрибут, который не виден снаружи, вы можете контролировать доступ к его значению, чтобы убедиться, что ваш объект всегда имеет допустимое состояние.

Давайте рассмотрим пример, чтобы лучше понять концепцию инкапсуляции.

Пример инкапсуляции в Python

Следующий код определяет класс Counter:

class Counter:
    def __init__(self):
        self.current = 0

    def increment(self):
        self.current += 1

    def value(self):
        return self.current

    def reset(self):
        self.current = 0

Класс Counter имеет один атрибут под названием current, который по умолчанию равен нулю. И у него есть три метода:

  • increment() увеличивает значение текущего атрибута на единицу;
  • value() возвращает текущее значение текущего атрибута;
  • reset() устанавливает значение текущего атрибута в ноль.

Следующий пример создает новый экземпляр класса Counter и трижды вызывает метод increment(), прежде чем отобразить текущее значение Counter на экране:

counter = Counter()


counter.increment()
counter.increment()
counter.increment()

print(counter.value())

Выход:

3

Он работает отлично, но есть одна проблема.

Извне класса Counter вы по-прежнему можете получить доступ к текущему атрибуту и изменить его на все, что захотите. Например:

counter = Counter()

counter.increment()
counter.increment()
counter.current = -999

print(counter.value())

Выход:

-999

В этом примере мы создаем экземпляр класса Counter, дважды вызываем метод increment() и устанавливаем для текущего атрибута недопустимое значение -999.

Так как же предотвратить изменение текущего атрибута за пределами класса Counter?

Для этого в игру вступают приватные атрибуты.

Приватные атрибуты

Приватные атрибуты могут быть доступны только из методов класса. Другими словами, они не могут быть доступны извне класса. В Python нет концепции приватных атрибутов. Другими словами, все атрибуты доступны снаружи класса.

По соглашению вы можете определить приватный атрибут, добавив к нему одиночное подчеркивание(_):

_attribute

Это означает, что атрибутом _attribute нельзя манипулировать, и в будущем он может иметь серьезные изменения.

Следующее по соглашению переопределяет класс Counter с текущим атрибутом в качестве закрытого:

class Counter:
    def __init__(self):
        self._current = 0

    def increment(self):
        self._current += 1

    def value(self):
        return self._current

    def reset(self):
        self._current = 0

Искажение имени двойным подчеркиванием

Если вы добавите к имени атрибута двойное подчеркивание(__), например:

__attribute

Python автоматически изменит имя __attribute на:

_class__attribute

В Python это называется name mangling (искажением имен).

Сделав это, вы не сможете получить доступ к __attribute напрямую извне класса, например:

instance.__attribute

Однако вы все равно можете получить к нему доступ, используя имя _class__attribute:

instance._class__attribute

В следующем примере класс Counter переопределяется с атрибутом __current:

class Counter:
    def __init__(self):
        self.__current = 0

    def increment(self):
        self.__current += 1

    def value(self):
        return self.__current

    def reset(self):
        self.__current = 0

Теперь, если вы попытаетесь получить доступ к атрибуту __current, вы получите сообщение об ошибке:

counter = Counter()
print(counter.__current)

Выход:

AttributeError: 'Counter' object has no attribute '__current'

Однако вы можете получить доступ к атрибуту __current как _Counter___current следующим образом:

counter = Counter()
print(counter._Counter__current)
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *