Наследование, MRO и магические методы
Продвинутый ООП-вопрос: множественное наследование, порядок MRO и ключевые dunder-методы.
MRO (Method Resolution Order) — порядок, в котором Python ищет метод по цепочке базовых классов. Строится алгоритмом C3 и доступен как
Class.__mro__.
Вопрос: какой метод вызовется при множественном наследовании?
Чёткий ответ. Python ищет метод по списку MRO слева направо. Для «ромбовидного» наследования C3-алгоритм гарантирует, что каждый класс встретится один раз и в логичном порядке.
class A:
def who(self): return "A"
class B(A):
def who(self): return "B"
class C(A):
def who(self): return "C"
class D(B, C):
pass
print(D().who()) # берётся из B (раньше в MRO)
print([cls.__name__ for cls in D.__mro__])
Вывод:
B ['D', 'B', 'C', 'A', 'object']
MRO для D: сам D, затем B, C, общий предок A и в конце object. Метод who берётся из B, потому что он раньше в списке.
Магические методы: __str__, __eq__, __hash__
Dunder-методы (double underscore) встраивают объект в язык. __str__ — человекочитаемое представление, __eq__ — сравнение через ==, __hash__ — чтобы объект можно было класть в множество и использовать ключом.
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
return f"({self.x}, {self.y})"
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
print(str(Point(1, 2)))
print(Point(1, 2) == Point(1, 2)) # __eq__
points = {Point(1, 2), Point(1, 2), Point(3, 4)}
print("уникальных точек:", len(points)) # дубликат схлопнулся
Вывод:
(1, 2) True уникальных точек: 2
Важное правило про __eq__ и __hash__
Если переопределяете __eq__, переопределяйте и __hash__ — иначе объект станет нехешируемым и не попадёт в множество. Равные объекты обязаны иметь одинаковый хеш.
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __hash__(self):
return hash(self.amount)
print(Money(100) == Money(100))
print(hash(Money(100)) == hash(Money(100)))
Вывод:
True True
Итог
- MRO задаёт порядок поиска метода; смотрите его через
Class.__mro__. __str__— печать,__eq__— сравнение,__hash__— для множеств и ключей словаря.- Переопределили
__eq__— переопределите и__hash__; равные объекты имеют равный хеш.