6.1. Инкапсуляция

Инкапсуляция — это объединение данных (атрибутов) и методов, работающих с этими данными, внутри одной сущности (класса/объекта). Также инкапсуляция подразумевает сокрытие внутреннего устройства объекта и предоставление контролируемого доступа к его данным.

Цели инкапсуляции:

  • Защита данных от случайного или некорректного изменения извне.
  • Упрощение интерфейса объекта (пользователю не нужно знать детали реализации).
  • Повышение модульности и гибкости (внутреннюю реализацию можно менять, не затрагивая код, использующий объект, если интерфейс сохранен).

Уровни Доступа в Python (Соглашения)

В Python нет строгих модификаторов доступа (public, private, protected), как в некоторых других языках (Java, C++). Вместо этого используются соглашения об именовании:

  1. Public (Публичный): Атрибуты и методы без префикса подчеркивания (my_attribute, my_method()). Доступны отовсюду. Это стандартный вариант.
  2. Protected (Защищенный): Имена с одним префиксом подчеркивания (_protected_attribute, _protected_method()). Это сигнал для других разработчиков, что данный атрибут/метод предназначен для внутреннего использования внутри класса или его дочерних классов, и его не следует использовать напрямую извне. Однако технически Python не запрещает доступ к нему.
  3. Private (Приватный): Имена с двумя префиксами подчеркивания (__private_attribute, __private_method()). Python применяет механизм “искажения имен” (name mangling): имя автоматически изменяется на _ClassName__private_name. Это затрудняет случайный доступ извне и предотвращает конфликты имен в дочерних классах, но не является настоящим сокрытием данных. Доступ все еще возможен по искаженному имени.
class MyClass:
    def __init__(self):
        self.public_var = "Я публичный"
        self._protected_var = "Я защищенный (по соглашению)"
        self.__private_var = "Я приватный (искажение имени)"
 
    def public_method(self):
        print("Публичный метод")
        self._protected_method()
        self.__private_method()
 
    def _protected_method(self):
        print("Защищенный метод")
 
    def __private_method(self):
        print("Приватный метод")
 
obj = MyClass()
 
# Доступ к public
print(obj.public_var)       # Я публичный
obj.public_method()         # Вызывает все три
 
# Доступ к protected (технически возможен, но не рекомендуется)
print(obj._protected_var)   # Я защищенный (по соглашению)
obj._protected_method()     # Защищенный метод
 
# Попытка доступа к private напрямую вызовет ошибку
print(obj.__private_var)  # AttributeError
obj.__private_method()    # AttributeError
 
# Доступ к private через искаженное имя (не рекомендуется!)
print(obj._MyClass__private_var) # Я приватный (искажение имени)
obj._MyClass__private_method()   # Приватный метод

Геттеры и Сеттеры

Традиционный способ управления доступом к атрибутам — через методы-геттеры (для получения значения) и сеттеры (для установки значения с возможной проверкой). В Python более “питоничным” способом являются свойства.

6.2. Абстракция

Абстракция — это процесс выделения наиболее важных характеристик объекта и игнорирования несущественных деталей. В ООП абстракция позволяет создавать классы, которые представляют общую концепцию, не вдаваясь в детали конкретной реализации.

Пользователю класса предоставляется только необходимый интерфейс (набор публичных методов и атрибутов), а сложность внутренней работы скрыта.

Абстрактные Базовые Классы (ABC - Abstract Base Classes)

В Python для формализации абстракции используется модуль abc. Абстрактный класс не может быть инстанциирован (нельзя создать его объект) и может содержать абстрактные методы.

  • Абстрактный метод: Метод, объявленный в абстрактном классе (с декоратором @abstractmethod), но не имеющий реализации. Все дочерние классы обязаны переопределить и реализовать все абстрактные методы родителя, иначе они тоже станут абстрактными.
from abc import ABC, abstractmethod
 
# Абстрактный базовый класс
class Shape(ABC):
    def __init__(self, name):
        self.name = name
 
    @abstractmethod # Декоратор для объявления абстрактного метода
    def area(self):
        pass # Нет реализации
 
    @abstractmethod
    def perimeter(self):
        pass
 
    def describe(self): # Обычный метод
        print(f"Это фигура: {self.name}")
 
# Попытка создать объект абстрактного класса вызовет ошибку
# shape = Shape("Фигура") # TypeError: Can't instantiate abstract class Shape...
 
# Конкретный дочерний класс, реализующий АБСТРАКТНЫЕ методы
class Square(Shape):
    def __init__(self, side):
        super().__init__("Квадрат")
        self.side = side
 
    def area(self): # Реализация обязательна
        return self.side * self.side
 
    def perimeter(self): # Реализация обязательна
        return 4 * self.side
 
# Другой конкретный класс
class Circle(Shape):
     pi = 3.14159
     def __init__(self, radius):
         super().__init__("Круг")
         self.radius = radius
 
     def area(self):
         return self.pi * (self.radius ** 2)
 
     def perimeter(self):
         return 2 * self.pi * self.radius
 
# Теперь можно создавать объекты дочерних классов
sq = Square(5)
ci = Circle(3)
 
sq.describe()   # Это фигура: Квадрат (унаследованный метод)
print(f"Площадь квадрата: {sq.area()}")       # 25
print(f"Периметр квадрата: {sq.perimeter()}") # 20
 
ci.describe()   # Это фигура: Круг
print(f"Площадь круга: {ci.area():.2f}")       # 28.27
print(f"Периметр круга: {ci.perimeter():.2f}") # 18.85

Абстрактные классы помогают определить общий интерфейс для группы классов и гарантировать, что все подклассы будут иметь необходимый набор методов.

〰〰〰 𓆝 𓆟 𓆞 𓆝 𓆟 𓆝 𓆟 𓆞 〰〰〰