Множественное наследование
Мы уже разобрались, что такое наследование в Python, как оно работает, его смысл и так далее. Но мы не разбирались с такой штукой, которая называется множественное наследование. Множественное отличается от обычного тем, что множественное имеет не одного родителя, а двух и более, то есть наследует более одного класса. У этого есть свои плюсы и свои минусы. Плюсы в том, что мы можем наследовать от множества классов, тем самым получая гибкость в проектировании классов и отношении между ними. А на счет минусов - сейчас мы разберем минусы этого самого множественного наследования на примерах.
Создали 3 класса - Animal, Carnivoure, Dog. Класс Dog наследует
Дальше мы создадим экземпляр Dog, затем погавкаем, поохотимся и умрем :( Результат будет ожидаемый:
Тут проблем нет, но давайте провигаться к ним дальше
опробуем создать некий deadly diamond of death, то есть смертельный ромб.
Создадим класс Animal, в котором определим метод set_health. Дальше создаем 2 класса, которые наследуются от Animal и используют тот же самый метод. А в конце создаем класс Dog, который наследуется от наших двух последних классов, которые наследуются от класса Animal. Тут как раз мы получили этот смертельный ромб. Если мы нарисуем диаграмму, и пририсуем линии наследования классов, то увидим, что от Carnivoure и Mammal идут линии до Animal, а от Dog идут линии к Animal и Carnivoure. В итоге мы получаем ромб:
Теперь смотрим, какие проблемы несет в себе этот ромбик.
Получили в ответе set on mammal. А как он выбрал, почему он не выдал нам set in carnivoure или оба? Здесь играет роль порядок наследования. Первым мы вписали Mammal, поэтому вывод получился из Mammal.
А что, если Dog тоже будет определять set_health, но хочет вызвать код из базовых классов?
По сути тут все ок. Но если каждый класс хочет вызвать метод из родительского?
А вот тут похоже есть какая то проблема :/ Почему получился такой вывод? Когда мы вызываем Mammal.set_health мы переходим в Mammal.set_health, который в свою очередь вызывает Animal.set_health, затем возвращаемся в set in mammal, дальше заходим в Carnivour, он вызывает Animal, опять set in animal, возвращаемся set in carnivour и уже дальше set in dog. Нам не нужно инициализировать базовый класс 2 раза. Это приведет к возникновению багов. Это проблема решается с помощью super
Теперь проблема исчезла. Такой прием используется в конструкторах, потому что зачастую класс наследник зависит от базового класса и опирается на какие то его методы и прежде чем использовать функционал базового класса наследник хотел бы его инициализировать, поэтому этот super мы зачастую будем прописывать именно при инициализации конструктора базового класса
Что касается множественного наследования, в следующий раз посмотрим на их примесь - миксины.