← Назад к вопросам
В какую сторону реализован поиск аргумента в наследовании в Python
2.3 Middle🔥 171 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Порядок поиска аргумента (атрибута) в наследовании Python
Краткий ответ
MRO (Method Resolution Order) — это порядок, в котором Python ищет методы и атрибуты в иерархии наследования. Реализован слева направо и глубина-поверхность-вперёд (C3 линеаризация).
Простой пример: линейное наследование
class A:
value = "A"
def method(self):
print("метод A")
class B(A):
value = "B"
class C(B):
pass
obj = C()
print(obj.value) # Вывод: "B"
print(obj.method()) # Вывод: метод A
# Порядок поиска для класса C:
# 1. C (сам класс)
# 2. B (родитель C)
# 3. A (родитель B)
# 4. object (корневой класс)
print(C.__mro__)
# (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
Сложный пример: множественное наследование
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):
pass
obj = D()
obj.method() # Вывод: B (не C!)
print(D.__mro__)
# D -> B -> C -> A -> object
# (слева направо: B перед C)
C3 линеаризация (алгоритм MRO)
Python использует C3 линеаризацию для вычисления MRO. Это гарантирует:
-
Слева направо — в классе
D(B, C)родители проверяются B → C → далее -
Глубина поверхность — сначала идём в глубину каждого пути
-
Монотонность — если A было перед B где-то, то везде A перед B
# Пример сложной иерархии:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
# Вычисление MRO для D:
# D + merge(
# [B, C, A], # D наследует от (B, C)
# [B, A], # B наследует от (A,)
# [C, A], # C наследует от (A,)
# [B, C] # порядок в определении D
# )
# → D
# → B (первый в списках, не блокирующий)
# → merge([C, A], [C, A], [C])
# → C (первый, не блокирующий)
# → merge([A], [A], [])
# → A (остаток)
# → object
Визуализация порядка поиска
# Диаграмма наследования:
#
# object
# |
# ┌────┴────┐
# | |
# A A
# | |
# B C
# \ /
# \ /
# \ /
# \ /
# D
# MRO для D: D → B → C → A → object
#
# Поиск идёт слева направо по диаграмме MRO:
# Атрибут? В D → нет
# Атрибут? В B → нет
# Атрибут? В C → нет
# Атрибут? В A → ДА!
Практические примеры
Пример 1: Поиск атрибута
class Animal:
sound = "generic sound"
class Dog(Animal):
sound = "woof"
class GoldenRetriever(Dog):
pass
dog = GoldenRetriever()
print(dog.sound) # Вывод: "woof"
# Порядок поиска:
# 1. GoldenRetriever.__dict__ (нет "sound")
# 2. Dog.__dict__ (ЕСТЬ "sound" = "woof")
# Результат: "woof"
Пример 2: Метод не определён в прямом классе
class Base:
def work(self):
return "base work"
class Child(Base):
pass
obj = Child()
print(obj.work()) # Вывод: "base work"
# Порядок поиска:
# 1. Child.work() (нет)
# 2. Base.work() (ЕСТЬ)
# Результат: "base work"
Пример 3: super() использует MRO
class A:
def greet(self):
return "A"
class B(A):
def greet(self):
return "B -> " + super().greet()
class C(A):
def greet(self):
return "C -> " + super().greet()
class D(B, C):
def greet(self):
return "D -> " + super().greet()
obj = D()
print(obj.greet()) # Вывод: "D -> B -> C -> A"
# super() следует MRO:
# D.greet() вызывает super().greet()
# → следующий в MRO после D это B
# B.greet() вызывает super().greet()
# → следующий в MRO после B это C
# C.greet() вызывает super().greet()
# → следующий в MRO после C это A
# A.greet() возвращает "A"
Пример 4: Мероприятие в множественном наследовании
class Mixin1:
def method(self):
print("Mixin1")
super().method()
class Mixin2:
def method(self):
print("Mixin2")
super().method()
class Base:
def method(self):
print("Base")
class Combined(Mixin1, Mixin2, Base):
pass
obj = Combined()
obj.method()
# Вывод:
# Mixin1
# Mixin2
# Base
# MRO:
print(Combined.__mro__)
# (<class 'Combined'>, <class 'Mixin1'>, <class 'Mixin2'>,
# <class 'Base'>, <class 'object'>)
Направление поиска (слева направо, вверх вниз)
Слева направо
class Parent1:
value = "P1"
class Parent2:
value = "P2"
class Child(Parent1, Parent2):
pass
# Parent1 перед Parent2, поэтому:
print(Child().value) # "P1"
# Поверни порядок:
class Child2(Parent2, Parent1):
pass
print(Child2().value) # "P2"
Вверх вверх (глубина в глубину)
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass
# MRO: D → B → C → A
# Сначала идём в глубину по B → A
# Потом по C → A
# Результат: B перед C
print(D().method()) # "B"
Когда MRO может быть невалидна (ошибка)
# Это вызовет ошибку! (невозможно линеаризовать)
try:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(A, B): # Конфликт! A перед B в определении
pass # но B наследует от A (должен быть B перед A)
except TypeError as e:
print(f"Ошибка: {e}")
# Вывод: "Cannot create a consistent method resolution"
Как проверить MRO на практике
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
# Способ 1: __mro__
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
# Способ 2: mro()
print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]
# Способ 3: help()
help(D)
# покажет MRO в начале
# Способ 4: inspect
import inspect
print(inspect.getmro(D))
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
Правила для запоминания
- Слева направо — в
class D(B, C)проверяем B перед C - Глубина первой — идём в глубину иерархии B перед тем как проверить C
- Монотонна — если A перед B, то везде A перед B
- C3 линеаризация — алгоритм вычисления этого порядка
- super() её использует —
super().method()ищет в MRO
Вывод
Порядок поиска атрибутов в наследовании Python:
- Слева направо — порядок родителей в определении класса
- Глубина в глубину — сначала вся иерархия B, потом C
- Реализовано через MRO (C3 линеаризация) — гарантирует консистентный и предсказуемый порядок
- Проверяемо через
.__mro__или.mro()