Глава 14 · Создаём объекты реального мира

Наследование

Когда один класс — это более частный случай другого, наследование позволяет не переписывать общее поведение заново.

Строим класс на основе существующего

Если два класса имеют много общего, но один — более общее понятие, а другой — его частный случай, отношение между ними — IS-A («Кошка — это животное»). Для такого отношения в Python есть наследование:

nasledovanie.py
class Zhivotnoe:
    def __init__(self, klichka):
        self.klichka = klichka

    def predstavitsya(self):
        print(f"Я {self.klichka}")


class Sobaka(Zhivotnoe):     # Sobaka — это Zhivotnoe
    def zvuk(self):
        return "Гав!"


class Koshka(Zhivotnoe):     # Koshka — это тоже Zhivotnoe
    def zvuk(self):
        return "Мяу!"


rex = Sobaka("Рекс")
rex.predstavitsya()    # унаследовано от Zhivotnoe — работает без переопределения
print(rex.zvuk())       # своё, определено в Sobaka
Zhivotnoe
Sobaka
Koshka
Sobaka и Koshka наследуют от Zhivotnoe — общее поведение (predstavitsya) не нужно писать дважды
SobakaZhivotnoeIS-A
IS-A: открытый треугольник указывает на родителя
Наследование — это переиспользование, а не обязанность
Sobaka получает predstavitsya() БЕСПЛАТНО, ничего не переписывая — в этом весь смысл. Но наследование оправдано только когда отношение реально IS-A. Если тянет унаследовать класс просто чтобы «одолжить» пару методов без настоящего родства понятий — это сигнал, что нужна композиция (раздел 14.13), а не наследование.
Debug Lab 8: забыли вызвать super().__init__()
propushchennyj_super.py
class Zhivotnoe:
    def __init__(self, klichka):
        self.klichka = klichka


class Sobaka(Zhivotnoe):
    def __init__(self, klichka, poroda):
        self.poroda = poroda     # klichka из Zhivotnoe не настроен!

rex = Sobaka("Рекс", "Дворняга")
print(rex.klichka)
Traceback (most recent call last):
  File "propushchennyj_super.py", line 9, in 
    print(rex.klichka)
AttributeError: 'Sobaka' object has no attribute 'klichka'
Что видно на экране

Когда дочерний класс определяет СОБСТВЕННЫЙ __init__, он полностью ЗАМЕНЯЕТ родительский __init__ — родительский код настройки атрибутов сам по себе больше не выполняется. Чтобы получить и то, и другое, дочерний __init__ должен явно вызвать родительский — разберём как именно, в разделе 14.16.

Исправленный код
s_super.py
class Sobaka(Zhivotnoe):
    def __init__(self, klichka, poroda):
        super().__init__(klichka)   # сначала настраиваем родительскую часть
        self.poroda = poroda
Практика: наследование
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки
Открыть практику →