ООП
Классы, наследование, MRO, дескрипторы, принципы SOLID и шаблоны проектирования.
50 вопросов
JuniorТеорияОчень частоЧто такое утиная типизация?
Что такое утиная типизация?
Это стиль, где пригодность объекта определяется методами и атрибутами, которые у него реально есть, а не его классом или наследованием: есть нужные методы — объект подходит. Это даёт полиморфизм без общего базового класса.
Типичные ошибки
- ✗Путать утиную типизацию со статической проверкой типов
- ✗Считать, что она требует наследования общего интерфейса или ABC
- ✗Думать, что дело в классе объекта, а не в его методах
Уточняющие вопросы
- →Как ABC с
__subclasshook__соотносится с утиной типизацией? - →Какая ошибка возникает, когда у утино-типизированного объекта нет метода?
JuniorТеорияОчень частоЧто такое инкапсуляция, и то же ли это, что сокрытие данных?
Что такое инкапсуляция, и то же ли это, что сокрытие данных?
Инкапсуляция объединяет данные с методами над ними в единое целое, держа связанные знания вместе и скрывая детали реализации. Сокрытие данных (ограничение доступа) — одна из её целей, но инкапсуляция шире простого сокрытия.
Типичные ошибки
- ✗Считать инкапсуляцию и сокрытие данных точными синонимами
- ✗Думать, что инкапсуляция требует приватных полей, навязанных языком
- ✗Забывать, что объединение методов с данными — её часть
Уточняющие вопросы
- →Как Python обозначает «приватный» атрибут, не навязывая это?
- →Что делает ведущее двойное подчёркивание с именем атрибута?
JuniorТеорияОчень частоКакие четыре столпа ООП?
Какие четыре столпа ООП?
Инкапсуляция объединяет данные с методами над ними и скрывает внутренности; наследование выводит один класс из другого; полиморфизм даёт одному интерфейсу много реализаций; абстракция показывает лишь существенные черты сущности.
Типичные ошибки
- ✗Называть лишь три столпа и забывать про абстракцию или инкапсуляцию
- ✗Путать инкапсуляцию (объединение и сокрытие) с наследованием (вывод классов)
- ✗Считать полиморфизм лишь перегрузкой операторов или методов
Уточняющие вопросы
- →Какой столп наиболее прямо поддерживает утиная типизация в Python?
- →Чем абстракция отличается от инкапсуляции на практике?
JuniorТеорияОчень частоЧто такое полиморфизм, и в каких формах он бывает?
Что такое полиморфизм, и в каких формах он бывает?
Полиморфизм даёт одному интерфейсу работать с разными типами. Формы: ad-hoc (перегрузка), параметрический (обобщения) и полиморфизм подтипов — производный тип через базовый с переопределёнными методами, самый частый. Python опирается на утиную типизацию.
Типичные ошибки
- ✗Сводить полиморфизм лишь к перегрузке
- ✗Считать, что Python требует общий базовый класс для полиморфизма
- ✗Путать параметрический (обобщения) с полиморфизмом подтипов
Уточняющие вопросы
- →Как утиная типизация даёт полиморфизм без наследования?
- →Почему Python не поддерживает перегрузку методов по сигнатуре?
JuniorТеорияОчень частоКак вызвать метод базового класса через super()?
Как вызвать метод базового класса через super()?
В Python 3 зовите super() без аргументов; он возвращает прокси, направляющий вызов к следующему классу в __mro__, поэтому super().__init__() запускает метод базового класса. Он кооперируется с множественным наследованием.
Типичные ошибки
- ✗Думать, что
super()всегда означает буквальный первый базовый класс - ✗Считать форму без аргументов недопустимой в Python 3
- ✗Забывать, что
super()следует за__mro__, а не за статической иерархией
Уточняющие вопросы
- →Почему
super()может достичь соседний класс, не входящий в прямую цепочку родителей? - →Что произойдёт, если пропустить
super().__init__()в подклассе?
MiddleТеорияОчень частоЧем абстракция отличается от инкапсуляции?
Чем абстракция отличается от инкапсуляции?
Абстракция — это проектное действие: показать лишь существенные черты и опустить неважные детали. Инкапсуляция — механизм, объединяющий данные с методами и прячущий внутренности. Абстракция — что показать, инкапсуляция — как спрятать.
Типичные ошибки
- ✗Считать абстракцию и инкапсуляцию одним и тем же
- ✗Думать, что абстракция требует явного
ABC - ✗Сводить абстракцию к модификаторам доступа у полей
Уточняющие вопросы
- →Бывает ли инкапсуляция без заметной абстракции?
- →Как
abc.ABCиProtocolвыражают абстракцию?
MiddleТеорияОчень частоПочему стоит предпочитать композицию наследованию?
Почему стоит предпочитать композицию наследованию?
Наследование жёстко связывает подкласс с базой и раскрывает её внутренности, делая иерархии хрупкими. Композиция (объект держит сотрудников и делегирует) гибче и заменяема в рантайме. Наследование — лишь для настоящего is-a.
Типичные ошибки
- ✗Брать наследование ради переиспользования кода, а не для is-a
- ✗Строить глубокие хрупкие иерархии, которые трудно менять
- ✗Считать, что композиция не способна дать полиморфизм
Уточняющие вопросы
- →Как делегирование позволяет менять сотрудника в рантайме?
- →Когда наследование всё же лучший выбор, чем композиция?
MiddleТеорияОчень частоЧто такое контекстный менеджер, и как его написать?
Что такое контекстный менеджер, и как его написать?
Это объект для with, определяющий __enter__ (его возврат привязан к as) и __exit__ (выполняется на выходе даже при исключении, получая тип, значение и traceback). Можно обернуть генератор через @contextlib.contextmanager.
Типичные ошибки
- ✗Думать, что
withпросто зовётclose(), а не протокол enter/exit - ✗Считать, что
__exit__пропускается при возникновении исключения - ✗Полагать, что нельзя построить его из генератора через
contextlib
Уточняющие вопросы
- →Что делает возврат
Trueиз__exit__с возникшим исключением? - →Как
contextlib.contextmanagerсопоставляетyieldс входом и выходом?
MiddleТеорияОчень частоПочему object() == object() даёт False, и чем отличаются is и ==?
Почему object() == object() даёт False, и чем отличаются is и ==?
По умолчанию == сводится к тождеству — стандартный __eq__ сравнивает id() — а два свежих объекта различны, поэтому результат False. is всегда проверяет тождество, а == проверяет равенство, настраиваемое через __eq__.
Типичные ошибки
- ✗Думать, что
==по умолчанию сравнивает значения полей - ✗Считать
isи==взаимозаменяемыми - ✗Забывать, что стандартный
__eq__сводится к сравнению тождества
Уточняющие вопросы
- →Почему переопределение
__eq__обычно требует и__hash__? - →Почему малые целые могут давать
Trueчерезisдаже при создании заново?
SeniorТеорияОчень частоЧем __new__ отличается от __init__, и в каком порядке они выполняются?
Чем __new__ отличается от __init__, и в каком порядке они выполняются?
__new__(cls, ...) создаёт и возвращает новый экземпляр и идёт первым; __init__(self, ...) инициализирует этот экземпляр и возвращает None, идя вторым. __init__ запускается, лишь если __new__ вернул экземпляр cls.
Типичные ошибки
- ✗Думать, что
__init__создаёт объект, а не инициализирует его - ✗Считать, что
__init__выполняется раньше__new__ - ✗Полагать, что
__new__обязан вернутьNone
Уточняющие вопросы
- →Что станет с
__init__, если__new__вернёт экземпляр другого класса? - →Как переопределение
__new__реализует паттерн синглтон?
JuniorТеорияЧастоКак определить абстрактный базовый класс через abc.ABC и что гарантирует @abstractmethod?
Как определить абстрактный базовый класс через abc.ABC и что гарантирует @abstractmethod?
Наследуйтесь от abc.ABC (или задайте metaclass=ABCMeta) и пометьте нужные методы декоратором @abstractmethod. Тогда Python запрещает создавать экземпляр базового класса и любого подкласса, не реализовавшего абстрактный метод, выбрасывая TypeError — это контракт, который обязаны выполнить конкретные подклассы.
Типичные ошибки
- ✗Думать, что один
abc.ABCделает методы абстрактными без@abstractmethod - ✗Считать, что обычный
raise NotImplementedErrorзапрещает создание экземпляра - ✗Ждать проверку абстрактных методов при импорте, а не при создании экземпляра
Уточняющие вопросы
- →Чем интерфейс на
abc.ABCотличается от структурногоProtocol? - →Можно ли сочетать
@abstractmethodс@propertyили@classmethod?
JuniorТеорияЧастоЧто означают DRY, KISS и YAGNI?
Что означают DRY, KISS и YAGNI?
DRY — Don't Repeat Yourself: выноси дублирование в одно переиспользуемое место. KISS — Keep It Simple: избегай ненужной хитрости. YAGNI — You Aren't Gonna Need It: не строй спекулятивные фичи, пока они реально не понадобятся.
Типичные ошибки
- ✗Понимать
YAGNIкак право вовсе пропускать нужное проектирование - ✗Применять
DRYтак рьяно, что рождается неверная абстракция - ✗Путать
KISS(простоту) с преждевременной оптимизацией
Уточняющие вопросы
- →Когда чрезмерный
DRYможет навредить дизайну? - →Как
YAGNIсоотносится с принципом открытости/закрытости?
JuniorТеорияЧастоЧто такое магические (dunder) методы в Python?
Что такое магические (dunder) методы в Python?
Это методы с именами в двойном подчёркивании — __init__, __len__, __add__ — которые интерпретатор вызывает неявно через встроенные функции, операторы или синтаксис: len(x) зовёт x.__len__(), a + b зовёт a.__add__(b).
Типичные ошибки
- ✗Называть их приватными методами, а не неявно вызываемыми хуками
- ✗Думать, что нужно явно звать
__len__или__add__, чтобы встроенные функции работали - ✗Считать, что двойное подчёркивание скрывает их от операторов и встроенных функций
Уточняющие вопросы
- →Что возвращает
__repr__и чем он отличается от__str__? - →Какой dunder-метод делает объект пригодным для цикла
for?
JuniorТеорияЧастоКак хранятся атрибуты экземпляра объекта?
Как хранятся атрибуты экземпляра объекта?
По умолчанию в собственном для экземпляра __dict__, отображающем имя в значение, поэтому атрибуты можно добавлять и удалять динамически во время выполнения. obj.__dict__ или vars(obj) показывают этот словарь.
Типичные ошибки
- ✗Думать, что атрибуты фиксируются при определении класса
- ✗Считать, что атрибуты экземпляра живут на классе, а не на экземпляре
- ✗Забывать, что
obj.__dict__раскрывает словарь экземпляра
Уточняющие вопросы
- →Как
__slots__меняет место хранения атрибутов экземпляра? - →Почему
dir(obj)показывает больше имён, чемobj.__dict__?
JuniorТеорияЧастоПоддерживает ли Python множественное наследование?
Поддерживает ли Python множественное наследование?
Да — класс может перечислять несколько базовых, например class A(B, C). Поиск метода и атрибута следует за __mro__ по C3, что включает миксины, но может давать неоднозначность, разрешаемую __mro__.
Типичные ошибки
- ✗Утверждать, что Python запрещает множественное наследование как Java
- ✗Думать, что первый базовый всегда выигрывает независимо от
__mro__ - ✗Считать, что конфликтующие методы из двух базовых сливаются и оба запускаются
Уточняющие вопросы
- →Чем миксины отличаются от обычного множественного наследования?
- →Какая ошибка возникает, когда базовые классы образуют несогласованную иерархию?
MiddleТеорияЧастоКакую задачу решает структурный паттерн adapter?
Какую задачу решает структурный паттерн adapter?
Оборачивает класс, чей интерфейс ты не можешь изменить, чтобы он совпал с интерфейсом, ожидаемым клиентом, переводя вызовы между ними. Позволяет несовместимому коду работать вместе — подогнать чужой API под свой интерфейс — не трогая исходники ни одной из сторон.
Типичные ошибки
- ✗Путать adapter (примиряет интерфейсы) с decorator (добавляет поведение)
- ✗Смешивать с facade, который упрощает целую подсистему
- ✗Править исходник адаптируемого класса вместо его обёртки
Уточняющие вопросы
- →Чем объектный adapter (композиция) отличается от классового (наследование)?
- →Почему утиная типизация часто заменяет adapter в Python?
MiddleКодЧастоЧто выведет это затенение атрибута класса?
Что выведет это затенение атрибута класса?
1 1, затем 1 2, затем 3 2. Сначала Child.x наследуется от Parent. Присваивание Child.x = 2 создаёт отдельный атрибут на Child, затеняющий родительский, поэтому последующая смена Parent.x на 3 уже не влияет на Child, который хранит собственную 2.
Типичные ошибки
- ✗Думать, что родитель и ребёнок всегда делят один атрибут
- ✗Считать, что запись атрибута распространяется вверх по MRO к родителю
- ✗Ожидать, что тень ребёнка очистится при переназначении родителя
Уточняющие вопросы
- →Где и в каком порядке идёт поиск атрибута по MRO?
- →Чем это отличается для изменяемого атрибута класса, например списка?
MiddleКодЧастоНапишите контекстный менеджер для очистки ресурса
Напишите контекстный менеджер для очистки ресурса
Используйте @contextlib.contextmanager: захватите до yield value (привязывается к as), а освобождение — в finally, чтобы очистка шла даже при исключении. Это эквивалент класса с __enter__ / __exit__. with гарантирует выполнение кода после yield.
Типичные ошибки
- ✗Опускать
try/finally, из-за чего очистка пропускается при исключении тела - ✗Использовать
returnвместоyieldв@contextmanager - ✗Освобождать до
yield, из-за чего тело выполняется после очистки
Уточняющие вопросы
- →Как
@contextmanagerсопоставляет код до/послеyieldс__enter__/__exit__? - →Что может
__exit__, чего блокуfinallyв@contextmanagerсделать непросто?
MiddleТеорияЧастоЧто делает структурный паттерн decorator (GoF)?
Что делает структурный паттерн decorator (GoF)?
Оборачивает объект в другой объект того же интерфейса, добавляя поведение во время выполнения и наслаивая обёртки без наследования. Каждая обёртка делегирует внутреннему объекту и добавляет своё, так что возможности компонуются динамически. Это не то же, что синтаксис @decorator в Python.
Типичные ошибки
- ✗Приравнивать паттерн decorator (GoF) к синтаксису
@decoratorв Python - ✗Путать с adapter, который меняет интерфейс, а не сохраняет его
- ✗Добавлять фичи взрывом подклассов вместо обёртки во время выполнения
Уточняющие вопросы
- →Как паттерн decorator избегает взрыва подклассов под комбинации фич?
- →Как декораторы
@в Python могут реализовать паттерн decorator?
MiddleТеорияЧастоКакую задачу решает порождающий паттерн factory method?
Какую задачу решает порождающий паттерн factory method?
Переносит создание объекта в метод (часто переопределяемый в подклассах), чтобы вызывающий просил продукт по намерению, а не по конкретному классу. Код зависит только от абстрактного продукта, поэтому новый вариант — это новый подкласс, а не правки в местах вызова.
Типичные ошибки
- ✗Путать его с singleton — factory method про то, какой класс, а не сколько экземпляров
- ✗Жёстко прописывать конкретный тип продукта вместо возврата абстрактного
- ✗Думать, что он избавляет от подклассов, а не опирается на них
Уточняющие вопросы
- →Чем factory method отличается от паттерна abstract factory?
- →Почему factory method часто не нужен в утиной типизации Python?
MiddleТеорияЧастоЧто такое миксин в Python?
Что такое миксин в Python?
Это небольшой вспомогательный класс, добавляемый в цепочку наследования ради конкретного поведения или методов; он не рассчитан на самостоятельное создание экземпляра. Технически это обычный класс, соглашение зовёт его ...Mixin.
Типичные ошибки
- ✗Думать, что
mixin— ключевое слово или особая конструкция - ✗Считать, что миксин требует метаклассов для работы
- ✗Воспринимать миксин как самостоятельный класс для создания экземпляра
Уточняющие вопросы
- →Где миксин должен стоять в списке базовых относительно основного класса?
- →Как миксин опирается на методы, которые сам не определяет?
MiddleТеорияЧастоКакую задачу решает поведенческий паттерн observer?
Какую задачу решает поведенческий паттерн observer?
Задаёт связь один-ко-многим: при изменении состояния субъекта все зарегистрированные наблюдатели уведомляются автоматически, обычно циклом notify, зовущим update каждого. Развязывает субъект и слушателей — субъект знает лишь интерфейс наблюдателя, а не конкретные типы.
Типичные ошибки
- ✗Думать, что наблюдатели опрашивают, а не получают push-уведомления
- ✗Путать со strategy, который подменяет алгоритм
- ✗Привязывать субъект к конкретным типам наблюдателей вместо интерфейса
Уточняющие вопросы
- →Как observer может вызвать утечки памяти, если наблюдателей не отписывать?
- →Как observer связан с колбэками Python или моделью pub/sub?
MiddleТеорияЧастоКак реализовать одиночку (singleton) в Python?
Как реализовать одиночку (singleton) в Python?
Гарантировать один общий экземпляр. Идиоматичные варианты: модуль (импортируется раз, потому естественный одиночка), metaclass с переопределённым __call__, кэширующий декоратор или переопределение __new__. Metaclass и модуль покрывают подклассы.
Типичные ошибки
- ✗Забывать, что модули сами по себе естественные одиночки
- ✗Блокировать второй экземпляр в
__init__, а не в__new__ - ✗Игнорировать потокобезопасность при ленивом создании экземпляра
Уточняющие вопросы
- →Почему одиночку часто ругают как скрытый глобал?
- →Как
__call__метакласса навязывает один экземпляр на класс?
MiddleТеорияЧастоКакую задачу решает поведенческий паттерн strategy?
Какую задачу решает поведенческий паттерн strategy?
Заключает семейство взаимозаменяемых алгоритмов за общим интерфейсом и позволяет клиенту выбрать или сменить один во время выполнения вместо жёстких веток if/elif. Контекст держит объект-стратегию и делегирует ему, поэтому новый алгоритм добавляется, не трогая контекст.
Типичные ошибки
- ✗Путать strategy (подмена алгоритма) с observer (уведомление слушателей)
- ✗Зашивать выбор алгоритма в подклассы вместо внедряемого объекта
- ✗Думать, что стратегию нельзя сменить после создания контекста
Уточняющие вопросы
- →Как strategy в Python часто оказывается просто функцией или
callable? - →Чем strategy отличается от похожего на вид паттерна state?
JuniorТеорияИногдаКакие категории паттернов проектирования существуют?
Какие категории паттернов проектирования существуют?
GoF делит их на три: порождающие (creation — factory, singleton, builder), структурные (композиция объектов — adapter, facade, decorator) и поведенческие (взаимодействие и ответственность — observer, strategy, iterator).
Типичные ошибки
- ✗Путать, какой паттерн к какой категории относится
- ✗Считать паттерны фичами языка, а не идеями дизайна
- ✗Предполагать, что каждый паттерн опирается на наследование
Уточняющие вопросы
- →К какой категории относится паттерн strategy?
- →Почему часть паттернов GoF избыточна в Python?
MiddleТеорияИногдаЧто даёт порождающий паттерн abstract factory?
Что даёт порождающий паттерн abstract factory?
Интерфейс для создания целых семейств связанных объектов без указания конкретных классов. Одна фабрика производит согласованный набор — например, все виджеты одной темы UI — поэтому замена фабрики разом меняет всё семейство и не даёт смешивать продукты разных семейств.
Типичные ошибки
- ✗Смешивать его с factory method, который делает один продукт, а не семейство
- ✗Путать с builder, который пошагово собирает один объект
- ✗Допускать смешивание продуктов из разных конкретных фабрик
Уточняющие вопросы
- →Когда лишняя абстракция abstract factory не оправдана?
- →Как factory method и abstract factory связаны друг с другом?
MiddleТеорияИногдаКакую задачу решает структурный паттерн bridge?
Какую задачу решает структурный паттерн bridge?
Разделяет абстракцию и её реализацию, чтобы они менялись независимо, держа реализацию через композицию, а не наследование. Это избегает взрыва классов, когда два ортогональных измерения — скажем, фигуры × движки отрисовки — иначе размножились бы в NxM подклассов.
Типичные ошибки
- ✗Путать bridge (развязка двух измерений) с adapter (примирение интерфейсов)
- ✗Использовать наследование там, где bridge требует композиции
- ✗Применять его, когда ось вариации всего одна
Уточняющие вопросы
- →Чем bridge отличается от strategy, который тоже компонует поведение?
- →Когда взрыв подклассов NxM оправдывает введение bridge?
MiddleТеорияИногдаКакую задачу решает порождающий паттерн builder?
Какую задачу решает порождающий паттерн builder?
Строит сложный объект пошагово через последовательность вызовов методов, отделяя то, как он собирается, от итогового представления. Добавляешь только нужные части (без телескопических конструкторов), затем зовёшь build(), чтобы получить готовый продукт.
Типичные ошибки
- ✗Путать builder (пошаговая сборка) с factory (выбор класса)
- ✗Возвращать или использовать продукт до того, как
build()его финализирует - ✗Применять его к простым объектам, которым не нужна поэтапная сборка
Уточняющие вопросы
- →Как builder устраняет проблему телескопических конструкторов?
- →Как текучий API с цепочкой методов связан с паттерном builder?
MiddleТеорияИногдаВ чём разница между связностью (cohesion) и связанностью (coupling)?
В чём разница между связностью (cohesion) и связанностью (coupling)?
Связность (cohesion) измеряет, насколько сфокусирован один модуль — высокая (делает одно чёткое дело) хороша. Связанность (coupling) измеряет взаимозависимость модулей — низкая (зависят минимально) хороша. Цель: высокая связность, низкая связанность.
Типичные ошибки
- ✗Менять местами определения связности и связанности
- ✗Забывать цель: высокая связность плюс низкая связанность
- ✗Считать эти два термина взаимозаменяемыми
Уточняющие вопросы
- →Как низкая связанность облегчает модульное тестирование?
- →Какой принцип
SOLIDпрямее всего повышает связность?
MiddleТеорияИногдаКакую задачу решает структурный паттерн facade?
Какую задачу решает структурный паттерн facade?
Ставит один упрощённый высокоуровневый интерфейс перед сложной подсистемой из многих классов, чтобы клиенты звали фасад, а не связывали части сами. Снижает связанность с внутренностями и даёт чистую точку входа, при этом оставляя прямой доступ к подсистеме, когда он нужен.
Типичные ошибки
- ✗Думать, что facade запрещает прямой доступ к подсистеме, а не просто даёт путь проще
- ✗Путать с adapter, который переводит один интерфейс в другой
- ✗Считать, что фасад обязан исчерпывающе оборачивать каждый класс подсистемы
Уточняющие вопросы
- →Чем facade отличается от adapter по намерению?
- →Может ли сам фасад стать god-объектом и как этого избежать?
MiddleТеорияИногдаЧто такое MRO, и что его определяет?
Что такое MRO, и что его определяет?
Method Resolution Order — линейный порядок, в котором Python ищет атрибут или метод по базовым классам. Он вычисляется C3-линеаризацией, доступен как Cls.__mro__ и совпадает с порядком, по которому идёт super().
Типичные ошибки
- ✗Описывать
__mro__как простой поиск в глубину слева направо - ✗Думать, что
__mro__пересчитывается при каждом доступе к атрибуту - ✗Забывать, что
super()направляет вызов по тому же__mro__
Уточняющие вопросы
- →Что C3-алгоритм гарантирует относительно локального приоритета?
- →Когда Python отказывается построить
__mro__и бросает ошибку?
MiddleТеорияИногдаВ чём разница между именами атрибутов _x и __x?
В чём разница между именами атрибутов _x и __x?
Одиночное подчёркивание (_x) — соглашение «внутреннее», доступ открыт. Двойное (__x) включает искажение имён: интерпретатор переписывает его в _ClassName__x, чтобы подклассы случайно не переопределили.
Типичные ошибки
- ✗Считать, что
__xпо-настоящему приватен и недоступен извне - ✗Думать, что
_xбросает ошибку при доступе извне - ✗Полагать, что
_xтоже подвергается искажению имён, как__x
Уточняющие вопросы
- →Как всё же добраться до искажённого
__xизвне класса? - →Почему хвостовой dunder вроде
__x__пропускает искажение имён?
MiddleТеорияИногдаКакую задачу решает структурный паттерн proxy?
Какую задачу решает структурный паттерн proxy?
Ставит объект-заместитель с тем же интерфейсом перед реальным объектом, чтобы контролировать доступ — добавляя ленивую загрузку, кэширование, проверки доступа или удалённые вызовы без изменения клиента. Клиент не отличает заместитель от настоящего субъекта, ведь интерфейс у них один.
Типичные ошибки
- ✗Путать proxy (контроль доступа, тот же интерфейс) с adapter (смена интерфейса)
- ✗Смешивать с наращиванием возможностей в decorator
- ✗Выставлять интерфейс, отличный от реального субъекта
Уточняющие вопросы
- →Как виртуальный proxy реализует ленивую инициализацию?
- →Чем proxy отличается от decorator, когда оба оборачивают объект?
MiddleТеорияИногдаЧего требует принцип единого уровня абстракции (SLAP)?
Чего требует принцип единого уровня абстракции (SLAP)?
Что все операторы внутри одной функции находятся на одном уровне абстракции — не смешивай высокоуровневую оркестрацию с низкоуровневой деталью в одном теле. Низкоуровневые шаги выносишь в именованные функции-помощники, чтобы каждая функция читалась как связный рассказ на одной высоте.
Типичные ошибки
- ✗Понимать «единый уровень» как буквальный лимит отступов или вложенности
- ✗Путать его с ограничением глубины наследования
- ✗Смешивать оркестрацию и детали в одной функции вместо выноса в помощники
Уточняющие вопросы
- →Как SLAP связан с тем, чтобы функции были короткими и хорошо названными?
- →Как SLAP и принцип DRY усиливают друг друга?
MiddleТеорияИногдаЧто обозначают принципы SOLID?
Что обозначают принципы SOLID?
SRP (единственная ответственность — одна причина для изменения), OCP (открыт для расширения, закрыт для модификации), LSP (подтипы заменяют базовый), ISP (узкие клиентские интерфейсы), DIP (зависеть от абстракций). Пять ООП-принципов Роберта Мартина.
Типичные ошибки
- ✗Путать значение одной из пяти букв
- ✗Путать
ISP(дробление интерфейсов) сSRP - ✗Думать, что
LSPзапрещает наследование, а не ограничивает его
Уточняющие вопросы
- →Приведи конкретный пример нарушения
LSP. - →Как
DIPсвязан с внедрением зависимостей?
SeniorТеорияИногдаКак алгоритм MRO C3-линеаризация решает проблему ромба?
Как алгоритм MRO C3-линеаризация решает проблему ромба?
C3 сливает __mro__ каждого базового и список базовых, храня локальный приоритет и монотонность. Для ромба A(B, C), где оба наследуют D, выходит [A, B, C, D, object]: D раз, после подклассов.
Типичные ошибки
- ✗Думать, что C3 идёт в глубину и
Dвстаёт сразу послеB - ✗Считать, что общий базовый дублируется в результате
- ✗Полагать, что полная цепочка левого базового берётся первой до
object
Уточняющие вопросы
- →Что гарантирует монотонность относительно
__mro__подкласса? - →Приведите порядок базовых, где C3 проваливается и бросает
TypeError.
SeniorТеорияИногдаЧто делает __slots__, и каковы его компромиссы?
Что делает __slots__, и каковы его компромиссы?
__slots__ объявляет фиксированный набор атрибутов и убирает __dict__ экземпляра, экономя память и слегка ускоряя доступ — ценно для миллионов экземпляров. Цена: нельзя добавлять атрибуты вне списка.
Типичные ошибки
- ✗Думать, что
__slots__отключает хуки__getattr__/__setattr__ - ✗Считать, что
__slots__делает атрибуты приватными - ✗Утверждать, что
__slots__не даёт выгоды по памяти
Уточняющие вопросы
- →Что происходит со
__slots__, когда подкласс опускает своё объявление? - →Как сохранить поддержку
__weakref__на классе со слотами?
SeniorТеорияИногдаЧего требует принцип инверсии зависимостей (DIP)?
Чего требует принцип инверсии зависимостей (DIP)?
Высокоуровневые модули не должны зависеть от низкоуровневых — оба зависят от абстракций; и абстракции не должны зависеть от деталей, детали зависят от абстракций. На практике ты внедряешь интерфейс, а не конкретный класс, отделяя политику от реализации.
Типичные ошибки
- ✗Путать инверсию зависимостей с инверсией наследования
- ✗Позволять одной конкретной реализации формировать абстракцию
- ✗Считать
DIPвыполненным лишь импортом интерфейса
Уточняющие вопросы
- →Кому должна принадлежать абстракция — высокому или низкому модулю?
- →Как типы
ProtocolподдерживаютDIPв Python?
MiddleТеорияРедкоЧто решает поведенческий паттерн chain of responsibility?
Что решает поведенческий паттерн chain of responsibility?
Передаёт запрос по цепочке обработчиков, пока один не обработает его, так что отправитель не знает, кто это сделает. Каждый обработчик либо обрабатывает запрос, либо передаёт следующему, развязывая отправителя и получателя и позволяя свободно менять порядок или добавлять обработчики.
Типичные ошибки
- ✗Думать, что выполняются все обработчики, а не останавливается на первом подходящем
- ✗Путать с одновременной рассылкой в observer
- ✗Привязывать отправителя к конкретному обработчику вместо цепочки
Уточняющие вопросы
- →Что происходит, если ни один обработчик цепочки не обработал запрос?
- →Чем middleware WSGI/Django похож на chain of responsibility?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн command?
Какую задачу решает поведенческий паттерн command?
Упаковывает запрос в объект, несущий действие и его аргументы, чтобы вызовы можно было ставить в очередь, логировать, передавать или отменять. Инициатор зовёт execute, не зная конкретной операции, развязывая того, кто просит действие, и того, кто его выполняет.
Типичные ошибки
- ✗Думать, что command — это просто прямой вызов метода без объекта-запроса
- ✗Путать с рассылкой событий в observer
- ✗Упускать, что овеществление запроса и даёт отмену и очередь
Уточняющие вопросы
- →Как паттерн command обеспечивает функциональность undo/redo?
- →Как
callableилиfunctools.partialв Python становятся лёгкой командой?
MiddleТеорияРедкоКакую задачу решает структурный паттерн composite?
Какую задачу решает структурный паттерн composite?
Складывает объекты в древовидные структуры и позволяет клиентам единообразно работать с отдельными листьями и целыми ветвями через один общий интерфейс. И папка, и файл имеют size(); папка просто рекурсивно обходит детей. Так чисто моделируются иерархии часть-целое.
Типичные ошибки
- ✗Давать листьям и составным узлам разные интерфейсы, заставляя клиента проверять тип
- ✗Путать его с линейной обёрткой в decorator
- ✗Забывать про рекурсию, благодаря которой работают операции над ветвями
Уточняющие вопросы
- →Как composite обрабатывает операцию, бессмысленную для листа?
- →Как composite связан с обходом дерева файловой системы?
MiddleТеорияРедкоКакую задачу решает структурный паттерн flyweight?
Какую задачу решает структурный паттерн flyweight?
Минимизирует память, разделяя один неизменяемый экземпляр общего внутреннего состояния между многими объектами, держа внешнее состояние снаружи. Тысячи спрайтов деревьев делят один объект текстуры; различаются лишь позиции. Интернирование строк и малых int в Python — встроенный пример.
Типичные ошибки
- ✗Делить изменяемое состояние вместо неизменяемого внутреннего
- ✗Путать с singleton — один объект всего против многих, делящих состояние
- ✗Забывать держать внешнее состояние снаружи flyweight
Уточняющие вопросы
- →Почему общее внутреннее состояние flyweight должно быть неизменяемым?
- →Как кэширование малых целых в CPython является flyweight?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн interpreter?
Какую задачу решает поведенческий паттерн interpreter?
Определяет иерархию классов, представляющую грамматику небольшого языка, и метод interpret для вычисления предложений на нём. Каждое правило грамматики — это тип узла; строится дерево выражения и рекурсивно вычисляется. Подходит для простых DSL вроде фильтров или арифметики.
Типичные ошибки
- ✗Путать паттерн interpreter с полноценным лексером/парсером/компилятором
- ✗Забывать, что он сочетается с composite для построения дерева выражения
- ✗Применять его для сложных грамматик, где нужен настоящий парсер
Уточняющие вопросы
- →Почему паттерн interpreter непрактичен для сложных грамматик?
- →Как паттерн interpreter связан с паттерном composite?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн mediator?
Какую задачу решает поведенческий паттерн mediator?
Централизует общение многих объектов в одном посреднике, чтобы они больше не ссылались друг на друга напрямую. Компоненты говорят с посредником, который их координирует, превращая запутанную сеть многие-ко-многим в звезду. Например, чат-комната, маршрутизирующая сообщения.
Типичные ошибки
- ✗Путать mediator (координирует участников) с observer (одностороннее уведомление)
- ✗Смешивать с ролью внешнего упрощения у facade
- ✗Оставлять прямые ссылки между компонентами на месте
Уточняющие вопросы
- →Как сам mediator может разрастись в god-объект?
- →Чем mediator отличается от observer, когда оба снижают связанность?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн memento?
Какую задачу решает поведенческий паттерн memento?
Захватывает внутреннее состояние объекта в снимок (мементо), который можно сохранить и позже восстановить, не раскрывая внутренностей объекта. Создатель создаёт мементо и восстанавливается из него, а смотритель лишь хранит их. Это основа undo/redo.
Типичные ошибки
- ✗Путать memento (снимок состояния) с command (овеществление операции)
- ✗Раскрывать внутренности создателя вместо непрозрачного мементо
- ✗Класть логику восстановления в смотрителя, а не в создателя
Уточняющие вопросы
- →Как memento лежит в основе undo/redo и как он связан с command?
- →Каковы затраты памяти на хранение множества мементо?
MiddleТеорияРедкоКакую задачу решает порождающий паттерн prototype?
Какую задачу решает порождающий паттерн prototype?
Создаёт новые объекты клонированием существующего экземпляра (прототипа) вместо создания с нуля по классу. Полезен, когда конструирование дорого или конкретный тип решается во время выполнения — копируешь готовый объект, часто через copy.deepcopy, и правишь его.
Типичные ошибки
- ✗Путать prototype (клонирование экземпляра) с factory (создание по классу)
- ✗Забывать о глубокой и поверхностной копии при вложенных изменяемых объектах
- ✗Думать, что он возвращает общий экземпляр, как singleton
Уточняющие вопросы
- →Когда prototype требует
deepcopy, а не поверхностныйcopy? - →Как протокол
__copy__/__deepcopy__в Python поддерживает prototype?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн state?
Какую задачу решает поведенческий паттерн state?
Позволяет объекту менять поведение при изменении внутреннего состояния, делегируя отдельному объекту-состоянию, который контекст подменяет во время выполнения. Заменяет разросшиеся if/elif по флагу статуса одним классом на состояние, каждый из которых задаёт переходы из себя.
Типичные ошибки
- ✗Путать state (контекст сам переходит) со strategy (клиент внедряет поведение)
- ✗Держать гигантский
if/elifпо флагу статуса вместо объектов-состояний - ✗Забывать, что каждое состояние задаёт свои исходящие переходы
Уточняющие вопросы
- →Чем state отличается от strategy, когда оба делегируют подменяемому объекту?
- →Где живёт логика переходов — в контексте или в объектах-состояниях?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн template method?
Какую задачу решает поведенческий паттерн template method?
Задаёт скелет алгоритма в методе базового класса, отдавая конкретные шаги на переопределение подклассам. Общая последовательность фиксирована; подклассы заполняют изменяемые части, не меняя структуру. Это «принцип Голливуда» — базовый класс вызывает тебя.
Типичные ошибки
- ✗Путать template method (наследование, фиксированный скелет) со strategy (композиция)
- ✗Позволять подклассам менять порядок шагов, а не просто заполнять их
- ✗Класть фиксированную последовательность в подклассы, а не в базовый метод
Уточняющие вопросы
- →Чем template method отличается от strategy в способе менять поведение?
- →Что такое «хук»-метод в template method и зачем делать его необязательным?
MiddleТеорияРедкоКакую задачу решает поведенческий паттерн visitor?
Какую задачу решает поведенческий паттерн visitor?
Позволяет добавлять новые операции к фиксированному набору классов-элементов, не меняя их, перенося операцию в объект-посетитель. Каждый элемент зовёт visitor.visit(self) (двойная диспетчеризация); новая операция — это новый посетитель, а не правки во всех классах элементов.
Типичные ошибки
- ✗Перепутать компромисс — visitor облегчает новые операции, а не новые типы элементов
- ✗Путать визит с двойной диспетчеризацией с простой итерацией
- ✗Править каждый класс-элемент вместо написания одного нового посетителя
Уточняющие вопросы
- →Почему добавление нового типа элемента вынуждает править каждого существующего посетителя?
- →Как двойная диспетчеризация заставляет visitor работать без проверок
isinstance?