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 шукає поля та методи у батьківських класів. Саме про це ви дізнаєтеся у наступній лекції.

Коментарі (4)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
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 Рівень 28
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 таке пофігістичне ставлення до якості лекцій, та студентів... Власне, вже не вперше це зауважую, дуже вже байдуже відношення до навчального матеріалу... "і так сойдьот"... (((