Множественное наследование в питоне
Содержание
Основная идея множественного наследования проста и понятна: точно так же, как при обычном наследовании потомок автоматом имеет все методы и свойства родителя, тем же образом мы хотим иметь возможность потомку указывать больше одного родителя и получать автоматом все метоты и свойства каждого из родителей. Синтаксис для этого в питоне простой:
Зачем нужно множественное наследование
Также, как при первом знакомстве с классами зачастую непонятно, зачем туда ещё и навешивать сверху такую штуку, как наследование, зачастую непонятно, зачем нужно множественное наследование, людям, которые привыкли программировать без него.
На эту тему есть примерно два ответа.
Во-первых, есть стиль программирования, при котором мы создаём по одному классу не на один однозначно определимый тип сущностей (как это случается, например, в классической иерархии видов в биологии), а на одно однозначно определимое проявление (поведение, свойство) сущности (подобно тому, как устроены определители растений или видов).
Например, если бы мы в такой парадигме делали библиотеку для рисования интерфейсов, мы бы создали класс Shape (фигура), сделали бы Rectangle потомком этого класса, создали бы класс WithText (то, на чём есть текст), и создали бы класс Clickable (то, на что можно нажать). И из этих классов мы могли бы сделать традиционную прямоугольную кнопку:
Некоторые люди умеют в этом стиле создавать большие библиотеки полуфабрикатов так, что потом для того, чтобы написать какую-нибудь, казалось бы, сложную задачу (например, веб-магазин), им достаточно унаследоваться от необходимого набора частей и добавить конфигурацию.
Второй ответ на вопрос, зачем нужно множественное наследование, примерно такой: если нам нужно сделать объект, который ведёт себя и как список, и как Tkinter.Canvas (например, поле для тетриса), то мы можем сделать новый класс, унаследовать его от одного из этих классов и добавить методов, чтобы он вёл себя дополнительно и как второй из этих классов, а можем просто унаследоваться сразу от обоих классов и добавить необходимого клея, чтобы состояние списка и состояние Canvas не рассинхронизировались.
Это не очень хороший пример (потому, что клей в этом случае очень нехорошо выглядит, и классы явно не предназначены для такого использования), но зато из жизни.
Если переформулировать, этот ответ можно сказать так: иногда множественное наследование – это просто удобно, чтобы сделать быстро и чтобы работало.
Проблемы множественного наследования
FIXME: если у обоих родителей есть одноимённые методы; как должен себя вести одноимённый метод ребёнка?
FIXME: если дед один, а родителей два (diamond diagram)
FIXME: разных сущностей – столько, сколько ребёнку кажется, что у него предков, или меньше (если кто-нибудь из них совпадает)? – это не риторический вопрос, оба ответа имеют смысл
Алгоритм C3
FIXME: линеаризация; какими свойствами хотелось бы, чтобы она обладала:
- FIXME: консистентность
- FIXME: сохранение локальных приоритетов
- FIXME: монотонность
FIXME: описание
FIXME: пример: линеаризация diamond diagram
FIXME: пример: нелинеаризуемое дерево наследования
Вызов методов родителей
FIXME: проблема метода init
Наивный подход
FIXME: наивный способ вызова методов родителей; его проблемы
super
FIXME: super; почему super устроен именно так (в питоне линеаризация хранится в классе.mro; в объекте хранится class_; в mro нужно найти, от какого места идти дальше) FIXME: проблемы смешения двух подходов; пример FIXME: набор правил, как лучше всего поступать в общем случае
FIXME: "атрибуты родителей" vs. "методы родителей"; слоёный пирог; есть ли здесь проблемы?
FIXME: из всего этого кажется, что множественное наследование – это очень сложно, но на самом деле, для того, чтобы упереться в какую-нибудь из проблем, нужно приложить очень много усилий В большинстве простых случаев проблемы просто не возникают, а в сложных случаях проблемы в худшем случае решаются введением правил поведения для некоторых ситуаций, некоторым количеством рефакторинга и, в худшем случае, созданием обёрток для сторонних классов (в исходный код которых мы не можем вмешаться).
Первая публикация с описанием алгоритма C3 (для языка Dylan) (более подробное и аргументированное описание алгоритма) Атрибуты объектов
Заключительное слово
Ссылки