JavaRush /Курси /Модуль 1: Python Core /Множинне наслідування

Множинне наслідування

Модуль 1: Python Core
Рівень 9 , Лекція 9
Відкрита

10.1 Наслідування від кількох класів

Множинне наслідування в Python дозволяє класу наслідувати атрибути та методи від більше ніж одного батьківського класу. Це дає більшу гнучкість і дозволяє повторно використовувати код, але також може призвести до складних ієрархій і потенційних конфліктів.

Можливість перелічити своєму класу п'ять батьків значно розширює ваші можливості та робить написання коду дуже зручним. Зробити це дуже просто — потрібно просто перелічити класи-батьки через кому:


class Base1:
    def method1(self):
        print("Method1 from Base1")
        
class Base2:
    def method2(self):
        print("Method2 from Base2")
        
class Derived(Base1, Base2):
    pass
        
obj = Derived()
obj.method1()
obj.method2()     
        

Все працює так, як і планувалось — краса.

Однак у множинного наслідування є кілька складних сторін, які обов'язково потрібно враховувати при його використанні. Давайте розглянемо, як це працює і як уникати проблем, пов'язаних з множинним наслідуванням.

10.2 Виклик методу, який є у кількох базових класів

Клас може наслідувати атрибути та методи від кількох батьківських класів, перелічених у дужках після імені класу. І ці атрибути й методи можуть мати однакові назви:


class Base1:
    def method(self):
        print("Method from Base1")
        
class Base2:
    def method(self):
        print("Method from Base2")
        
class Derived(Base1, Base2):
    pass
        

obj = Derived()
obj.method() # чий метод тут викличеться?
        

У цьому прикладі Derived клас наслідується від Base1 і Base2. Коли викликається method(), Python вибере метод першого вказаного класу — Base1.

Але це не так очевидно, чи не так? І якщо хтось змінить код базового класу, може постраждати вся логіка програми, і ви навіть не дізнаєтеся, що щось не так. Просто почнуть викликатися трохи не ті методи :)

10.3 Використання super() з множинним наслідуванням

Ще одна цікава особливість — це виклик super() для базового класу при множинному наслідуванні.

Приклад:


class Base1:
    def method(self):
        print("Method from Base1")
        super().method()
        
class Base2:
    def method(self):
        print("Method from Base2")
        super().method()
        
class Derived(Base1, Base2):
    def method(self):
        print("Method from Derived")
        super().method()
         

obj = Derived()
obj.method()
        

Ну що? Який буде вивід на екран?


Method from Derived
Method from Base1
        

Або


Method from Derived
Method from Base2
        

У мене для вас сюрприз — вивід буде таким:


Method from Derived
Method from Base1
Method from Base2
        

Код super().method() викличе метод method() у кожного базового класу! Це якраз і є ті самі нюанси, про які я говорив при першому згадуванні множинного наслідування.

10.4 Ромбоподібне (Diamond) наслідування

І нарешті, класична проблема ромбоподібного наслідування. Її легше показати на прикладі, ніж описувати:

У вигляді коду це може виглядати, наприклад, так:


class A:
    def method(self):
        print("Method from A")
        
class B(A):
    def method(self):
        print("Method from B")
        super().method()
        
class C(A):
    def method(self):
        print("Method from C")
        super().method()
        
class D(B, C):
    def method(self):
        print("Method from D")
        super().method()
         
         
        
obj = D()
obj.method()
        

Вивід буде таким:


Method from D
Method from B
Method from C
Method from A
   
        

Щоб хоч якось орієнтуватися в множинному наслідуванні, вам потрібно добре знати, в якому порядку Python шукає поля та методи у батьківських класів. Саме про це ви дізнаєтеся у наступній лекції.

Коментарі (6)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Андрій Рівень 29
2 травня 2026
У прикладі 10.3 помилка. Варіант з трьома спрацюванням методів: class Base1: def method(self): print("Method from Base1") # super().method() class Base2: def method(self): print("Method from Base2") super().method() class Derived(Base2, Base1): def method(self): print("Method from Derived") super().method() obj = Derived() obj.method() # Method from Derived # Method from Base2 # Method from Base1
negoda Рівень 31
12 серпня 2025
Вторая задача:

class BaseA:
    def action(self):
        print(1)
class BaseB:
    def action(self):
        print(2)
class Derived(BaseA, BaseB):
    def action(self):
        print(3)
        super().action()

a = Derived()
a.action()
Вообще не понял смысл зачем нам два суперкласса, если задача не проходит проверку в том случае если вызывается super() из каждого класса... Обьясню :

class BaseA:
    def action(self):
        print(1)
class BaseB:
    def action(self):
        print(2)
        super().action()
class Derived(BaseB, BaseA):
    def action(self):
        print(3)
        super().action()

a = Derived()
a.action()
В таком варианте записи, у нас на выводе 3, 2, 1, тоесть мы вызываем метод супер в классе Derived и BaseB, но не в BaseA, чтобы не было ошибки, и при этом наследование Derived(BaseB, BaseA) а не Derived(BaseA. BaseB). Во втором варианте записи код выполняется корректно, и используются все два родительских класса, но такой код не проходит проверку, просто потому что ДжаваРашу хочется чтобы было Derived(BaseA. BaseB), и чтобы метод super() был только в классе Derived, но в таком случае мы на выводе получаем 3,1 , и второй класс вообще никак не задействуется, тогда вопрос - зачем он вообще здесь нужен? Странно, очень странно хаха
negoda Рівень 31
12 серпня 2025

class Base1:
    def method1(self):
        print("Method1 from Base1")

class Base2:
    def method2(self):
        print("Method2 from Base2")

class Derived(Base1, Base2):
    pass

obj = Derived()
obj.method1()
obj.method2()
Пришлось гуглить почему в обьявлении первых двух классов перед двоеточием нет привычных скобок, оказывается что если мы не собираемся делать так чтобы класс наследовал какой либо другой класс, а был самостоятельным или сам являлся родительским - скобки ставить не обязательно. Очень странно что до этого нас не утрудили этой информацией, а когда впервые такую запись использовали сдесь - не обьяснили почему. Мда
Lexx23021074 Рівень 64
28 травня 2025
З superom в 10.3 такий фокус не пройде викине помилку. Для того щоб був такий вивід треба розірвати ланцюжок super, наприклад своривши пустий клас і перші два методи зробити від цього пустого батьківського: class Root: def method(self): pass class Base1(Root): def method(self): print("Method from Base1") super().method() class Base2(Root): def method(self): print("Method from Base2") super().method() class Derived(Base1, Base2): def method(self): print("Method from Derived") super().method() obj = Derived() obj.method()
Semen Рівень 21
7 липня 2025
Як,я розумію, лекції ніхто виправити не збирається...? Шкода, що в JavaRush таке пофігістичне ставлення до якості лекцій, та студентів... Власне, вже не вперше це зауважую, дуже вже байдуже відношення до навчального матеріалу... "і так сойдьот"... (((
Gena Рівень 34
26 квітня 2026
Я теж попася на це, викидує помилку, не розумію чому. Тепер виправив