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), виклик завершиться без подальших дій.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ