super() и порядок разрешения методов
super() позволяет дочернему классу воспользоваться методом родителя изнутри переопределения — а не заново писать его с нуля.
super(): позвать родителя, а не заменить его
super() даёт доступ к методам родительского класса ИЗНУТРИ
переопределённого метода — обычно чтобы ДОПОЛНИТЬ поведение родителя, а не заменить его
целиком:
class Zhivotnoe:
def __init__(self, klichka):
self.klichka = klichka
class Sobaka(Zhivotnoe):
def __init__(self, klichka, poroda):
super().__init__(klichka) # выполняет Zhivotnoe.__init__(self, klichka)
self.poroda = poroda # затем добавляет своё
rex = Sobaka("Рекс", "Дворняга")
print(rex.klichka, rex.poroda) # Рекс Дворняга — оба атрибута на месте
super() возвращает специальный промежуточный объект (прокси), который умеет находить методы РОДИТЕЛЯ для ТЕКУЩЕГО self — self остаётся объектом класса Sobaka, он не превращается в Zhivotnoe. Просто поиск метода на этот раз начинается не с Sobaka, а на уровень выше.Порядок разрешения методов (MRO)
При обычном одиночном наследовании порядок поиска метода простой и предсказуемый:
сначала сам класс объекта, затем его родитель, затем родитель родителя — и так до
object, базового класса для всех классов в Python:
class Sobaka(Zhivotnoe):
def __init__(self, klichka, poroda):
self.poroda = poroda
print(f"Порода: {self.poroda}, кличка: {self.klichka}") # klichka ещё нет!
super().__init__(klichka)
Traceback (most recent call last):
print(f"Порода: {self.poroda}, кличка: {self.klichka}")
AttributeError: 'Sobaka' object has no attribute 'klichka'
super().__init__(klichka) стоит ПОСЛЕ строки, которая уже пытается прочитать self.klichka — на этот момент родительский __init__ ещё не выполнялся, атрибут ещё не создан. Порядок вызовов внутри __init__ исполняется строго сверху вниз, как и в любой другой функции.
class Sobaka(Zhivotnoe):
def __init__(self, klichka, poroda):
super().__init__(klichka) # сначала родительская часть
self.poroda = poroda
print(f"Порода: {self.poroda}, кличка: {self.klichka}")