Фундаментальные принципы ООП: инкапсуляция, наследование и полиморфизм
Три столпа ООП: сокрытие внутреннего состояния и геттеры/сеттеры (@property), иерархия классов через наследование и super(), динамический полиморфизм и утиная типизация.
Инкапсуляция решает две ключевые задачи: объединение данных и методов в одну оболочку и ограничение прямого несанкционированного доступа к внутреннему состоянию объекта извне. Это защищает систему от перевода объекта в некорректное состояние (например, установку отрицательного баланса или недопустимой маски подсети).
В Python в отличие от C++ или Java нет ключевых слов private/protected на уровне компилятора. Вместо этого действуют строгие соглашения и механизм изменения имен (Name Mangling):
- Публичные атрибуты (public): name — доступны для чтения и записи отовсюду.
- Защищенные атрибуты (protected): _name (один знак подчеркивания) — соглашение для разработчиков о том, что атрибут предназначен только для внутреннего использования классом и его наследниками.
- Приватные атрибуты (private): __name (два знака подчеркивания) — имя атрибута автоматически преобразуется интерпретатором в _ClassName, name, что защищает его от случайной перезаписи в дочерних классах.
Инкапсуляция (Encapsulation)
Name Mangling
Защищенность в Python держится на соглашениях
В сообществе Python действует принцип 'Мы все тут взрослые люди' (We are all consenting adults here). Символ подчеркивания _ говорит разработчику: 'Не трогай это поле напрямую, используй публичные методы'.
В классических языках для защиты полей пишут многословные методы getbalance() и setbalance(). В Python существует элегантный и 'питонический' инструмент — свойство (декоратор @property).
Декоратор @property превращает метод чтения в виртуальный атрибут. Обращение к obj.salary синтаксически выглядит как доступ к обычной переменной, но 'под капотом' прозрачно запускает функцию-геттер.
Сеттер (@attribute.setter) перехватывает попытку записи значения (obj.salary = 100000) и позволяет провести строгую валидацию входных данных: проверить тип, допустимый диапазон или права доступа, выбрасывая исключение ValueError при нарушении бизнес-правил.
Наследование позволяет создать новый класс на основе уже существующего базового (родительского) класса. Дочерний класс (подкласс) автоматически наследует все атрибуты и методы родителя, а также может дополнять их новыми полями или переопределять существующие методы (Method Overriding).
Для корректной инициализации унаследованных свойств в конструкторе подкласса обязательно вызывается функция super().__init, (...). Она делегирует вызов родительскому конструктору, предотвращая дублирование кода инициализации.
При множественном наследовании (когда класс наследует сразу от двух и более родителей) Python использует строгий алгоритм C3-линеаризации для формирования порядка разрешения методов (MRO — Method Resolution Order), доступного через ClassName.__mro, .
Полиморфизм (от греческого 'многообразие форм') — это способность программы единообразно обрабатывать объекты разных классов, если они предоставляют одинаковый публичный интерфейс (набор методов с одинаковыми именами и сигнатурами).
В динамически типизированном Python полиморфизм возведен в абсолют через концепцию утиной типизации (Duck Typing): "Если нечто плавает как утка, крякает как утка и ходит как утка, то для нас это утка". Программе не обязательно требовать строгой иерархии наследования от общего абстрактного предка — достаточно, чтобы вызываемый метод существовал у передаваемого объекта.
Благодаря полиморфизму код становится предельно гибким: можно написать функцию отправки уведомлений notifyall(clients), которая итеративно вызывает client.send(msg) у объектов классов EmailClient, TelegramBot, SMSGateway, не зная деталей их реализации.
