JavaRush /Курси /Модуль 1: Python Core /Порядок вирішення методів (MRO)

Порядок вирішення методів (MRO)

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

11.1 Method Resolution Order

Порядок вирішення методів (Method Resolution Order, MRO) визначає послідовність, у якій Python шукає методи та атрибути в ієрархії класів. Це особливо важливо при роботі з множинним наслідуванням, коли клас може наслідувати атрибути та методи від кількох батьківських класів.

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

Алгоритм C3-лінеризації

Алгоритм C3-лінеризації визначає MRO шляхом комбінування:

  • Самого класу.
  • Списку батьківських класів у порядку їхнього перерахування.
  • MRO батьківських класів у тому ж порядку.

Правила алгоритму C3-лінеризації

  • Зберігати локальний порядок методів: якщо клас A вказаний перед класом B, всі методи класу A повинні розглядатися перед методами класу B.
  • Підтримувати порядок у батьківських класах: якщо клас A є батьком класу B, то всі методи класу A повинні розглядатися перед методами класу B.
  • Враховувати порядок наслідування: якщо клас C є батьком для двох або більше класів, порядок методів класу C повинен зберігатися в MRO усіх цих класів.

Кроки алгоритму:

Крок 1. Почнемо з самого класу:

Завжди починаємо з самого класу, в якому викликано метод.

Крок 2. Додаємо базові класи у порядку їхнього перерахування:

Після поточного класу перевіряємо базові класи у тому порядку, в якому вони вказані при наслідуванні.

Крок 3. Обходимо батьківські класи:

Шукаємо поля і методи там.

Крок 4. Об'єднуємо MRO батьківських класів:

Якщо один і той самий базовий клас наслідується через кілька шляхів, він перевіряється лише один раз і в правильному порядку (всі інші рази він буде пропущений).

Для тих, хто вже знайомий з темою «Алгоритми і структури даних», це пошук у глибину, а не в ширину.

11.2 Перевірка MRO

У Python можна перевірити порядок обходу методів і полів класу, використовуючи атрибут __mro__ або функцію mro().

Приклад:


class A:
    def method(self):
        print("A")
        
class B(A):
    def method(self):
        print("B")
        
class C(A):
    def method(self):
        print("C")
        
class D(B, C):
    def method(self):
        print("D")
        

# Перевірка MRO
print(D.__mro__)
        

Вивід буде:


(<class '__main__.D'>, 
<class '__main__.B'>, 
<class '__main__.C'>,
<class '__main__.A'>,
<class 'object'>)
        

Це показує порядок, у якому Python буде шукати методи та атрибути:

  • D: Python спочатку перевіряє метод у класі D.
  • B: Потім Python перевіряє метод у класі B (перший батьківський клас).
  • C: Якщо метод не знайдено в класі B, Python перевіряє метод у класі C (другий батьківський клас).
  • A: Якщо метод не знайдено у класах B і C, Python перевіряє метод у класі A.
  • object: І на кінець, Python перевіряє метод у базовому класі object.

11.3 Використання super() з MRO

Функція super() слідує MRO для виклику методів батьківських класів у правильному порядку. Розглянемо приклад використання super():

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

Вивід буде наступним:


D
B
C
A
        

Порядок обходу (MRO)

1. Виклик методу method класу D:

  • Python спочатку перевіряє метод у класі D і знаходить його там.
  • Метод D.method() виконується і друкує "D".
  • Потім викликається super().method(), який слідує MRO для виклику наступного методу.

2. Виклик методу method класу B:

  • Згідно з MRO, наступний клас після D — це B.
  • Метод B.method() виконується і друкує "B".
  • Потім викликається super().method(), який слідує MRO для виклику наступного методу.

3. Виклик методу method класу C:

  • Наступний клас в MRO після B — це C.
  • Метод C.method() виконується і друкує "C".
  • Потім викликається super().method(), який слідує MRO для виклику наступного методу.

4. Виклик методу method класу A:

  • Наступний клас в MRO після C — це A.
  • Метод A.method() виконується і друкує "A".
  • Потім викликається super().method(), але так як у A немає батьківських методів method (крім object), виклик завершиться без подальших дій.
1
Опитування
Наслідування, рівень 9, лекція 10
Недоступний
Наслідування
Наслідування
Коментарі (6)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Oleksandr Рівень 21
15 травня 2025
Що таке "C3-лінеризація" ? Бракує визначення в лекції
negoda Рівень 31
12 серпня 2025
C — означает "consistent" 3 — потому что алгоритм объединяет (линеаризует) три источника информации при построении порядка: Линеаризацию каждого из родителей (их MRO) Прямой список родителей (то, что указано при объявлении класса) Сам класс, для которого строится MRO
Nataliia Fedyshyn Рівень 1
19 січня 2025

class A:
    def method(self):
        print("A")
        super().method()

class B(A):
    def method(self):
        print("B")
        super().method()

class C(A):
    def method(self):
        print("C")
        super().method()

class D(B, C):
    def method(self):
        print("D")
        super().method()


d = D()
d.method()
вивід буде наступний: D B C A Traceback (most recent call last): File "/Users/fedytion/javarush/3538753/javarush-project/src/ua/javarush/python/core/level09/task16/task16.py", line 31, in <module> d.method() File "/Users/fedytion/javarush/3538753/javarush-project/src/ua/javarush/python/core/level09/task16/task16.py", line 27, in method super().method() File "/Users/fedytion/javarush/3538753/javarush-project/src/ua/javarush/python/core/level09/task16/task16.py", line 17, in method super().method() File "/Users/fedytion/javarush/3538753/javarush-project/src/ua/javarush/python/core/level09/task16/task16.py", line 22, in method super().method() File "/Users/fedytion/javarush/3538753/javarush-project/src/ua/javarush/python/core/level09/task16/task16.py", line 12, in method super().method() ^^^^^^^^^^^^^^ AttributeError: 'super' object has no attribute 'method' І ніхто не пояснює чому.. Навіщо давати в приклад код з помилками? 😬
Nataliia Fedyshyn Рівень 1
19 січня 2025
Я вже розібралась чому помилка, не можна викликати метод super() в "головного" батьківського класу, бо він не може сам собі бути батьківським класом. Але все ж, це збиває з пантелику. Виправте, будь ласка і уточніть в лекції це.
Василь Рівень 44
8 квітня 2025
У будь якокго класу батьківським є object. В наведеному прикладі в класу A є зайвим виклик super().method() тому що він і викликає клас object в якому немає методу method() ось тому і помилка. Правильніше переписати клас А: class A: def method(self): print("A")
Дмитро Рівень 31
23 квітня 2025
Досі не виправили, ще і помилку видає (при перевірці) коли class A: def method(self): print("A") #super().method() А у відповіді class A: def method(self): print("A") # No call to super() because A is the topmost class Таки вказано що не потрібно викликати super() Чекаємо коли виправлять автоматичну перевіку)