Понятие наследования

Наследование - один из основных принципов объектно-ориентированного программирования. Оно позволяет создавать новые классы на основе существующих, наследуя их атрибуты и методы. Это способствует повторному использованию кода и созданию иерархий классов.

Ключевые понятия наследования

Базовое наследование

Для создания дочернего класса указывается родительский класс в скобках после имени класса:

Простое наследование
# Родительский класс
class Животное:
    def __init__(self, имя, возраст):
        self.имя = имя
        self.возраст = возраст
    
    def описание(self):
        return f"{self.имя} - животное, возраст: {self.возраст} лет"
    
    def спать(self):
        return f"{self.имя} спит"
    
    def есть(self):
        return f"{self.имя} ест"

# Дочерний класс
class Собака(Животное):
    def __init__(self, имя, возраст, порода):
        # Вызов конструктора родительского класса
        super().__init__(имя, возраст)
        self.порода = порода
    
    # Новый метод
    def лаять(self):
        return f"{self.имя} гавкает"
    
    # Переопределение метода
    def описание(self):
        return f"{self.имя} - собака породы {self.порода}, возраст: {self.возраст} лет"

# Еще один дочерний класс
class Кошка(Животное):
    def __init__(self, имя, возраст, цвет):
        super().__init__(имя, возраст)
        self.цвет = цвет
    
    def мяукать(self):
        return f"{self.имя} мяукает"
    
    def описание(self):
        return f"{self.имя} - кошка цвета {self.цвет}, возраст: {self.возраст} лет"

# Использование классов
собака = Собака("Бобик", 3, "Овчарка")
кошка = Кошка("Мурка", 2, "Рыжий")

# Унаследованные методы
print(собака.спать())
print(кошка.есть())

# Переопределенные методы
print(собака.описание())
print(кошка.описание())

# Собственные методы
print(собака.лаять())
print(кошка.мяукать())

Множественное наследование

Python поддерживает множественное наследование, когда класс может наследовать от нескольких родительских классов:

Множественное наследование
# Первый родительский класс
class Летающий:
    def __init__(self, высота=0):
        self.высота = высота
    
    def взлететь(self):
        self.высота = 100
        return f"Объект взлетел на высоту {self.высота} метров"
    
    def приземлиться(self):
        self.высота = 0
        return "Объект приземлился"

# Второй родительский класс
class Плавающий:
    def __init__(self, глубина=0):
        self.глубина = глубина
    
    def нырнуть(self):
        self.глубина = 10
        return f"Объект нырнул на глубину {self.глубина} метров"
    
    def всплыть(self):
        self.глубина = 0
        return "Объект всплыл"

# Дочерний класс, наследующий от двух родительских
class Утка(Летающий, Плавающий):
    def __init__(self, имя):
        # Вызов конструкторов обоих родительских классов
        Летающий.__init__(self)
        Плавающий.__init__(self)
        self.имя = имя
    
    def описание(self):
        return f"{self.имя} - утка, может летать и плавать"

# Использование множественного наследования
утка = Утка("Дональд")
print(утка.описание())

# Методы от первого родительского класса
print(утка.взлететь())
print(утка.приземлиться())

# Методы от второго родительского класса
print(утка.нырнуть())
print(утка.всплыть())

Метод super()

Метод super() используется для вызова методов родительского класса:

Использование super()
class ТранспортноеСредство:
    def __init__(self, марка, модель, год):
        self.марка = марка
        self.модель = модель
        self.год = год
        print(f"Создано транспортное средство: {self.марка} {self.модель}")
    
    def описание(self):
        return f"{self.марка} {self.модель} ({self.год})"
    
    def завести(self):
        return "Транспортное средство заведено"

class Автомобиль(ТранспортноеСредство):
    def __init__(self, марка, модель, год, тип_двигателя):
        # Вызов конструктора родительского класса через super()
        super().__init__(марка, модель, год)
        self.тип_двигателя = тип_двигателя
        print(f"Добавлен тип двигателя: {self.тип_двигателя}")
    
    def описание(self):
        # Вызов метода родительского класса через super()
        базовое_описание = super().описание()
        return f"{базовое_описание}, двигатель: {self.тип_двигателя}"
    
    def завести(self):
        # Вызов метода родительского класса и его расширение
        базовое_сообщение = super().завести()
        return f"{базовое_сообщение} - автомобиль {self.марка}"

class Электромобиль(Автомобиль):
    def __init__(self, марка, модель, год, емкость_батареи):
        # Вызов конструктора родительского класса
        super().__init__(марка, модель, год, "электрический")
        self.емкость_батареи = емкость_батареи
        print(f"Добавлена емкость батареи: {self.емкость_батареи} кВт·ч")
    
    def описание(self):
        # Цепочка вызовов через super()
        базовое_описание = super().описание()
        return f"{базовое_описание}, батарея: {self.емкость_батареи} кВт·ч"
    
    def зарядить(self):
        return f"Электромобиль {self.марка} заряжается"

# Использование цепочки наследования
электромобиль = Электромобиль("Tesla", "Model S", 2023, 100)
print(электромобиль.описание())
print(электромобиль.завести())
print(электромобиль.зарядить())

Абстрактные классы

Абстрактные классы определяют интерфейс для своих подклассов, но не могут быть инстанцированы напрямую:

Абстрактные классы
from abc import ABC, abstractmethod

# Абстрактный класс
class Фигура(ABC):
    def __init__(self, название):
        self.название = название
    
    @abstractmethod
    def площадь(self):
        pass
    
    @abstractmethod
    def периметр(self):
        pass
    
    def описание(self):
        return f"Это {self.название}"

# Конкретные реализации
class Прямоугольник(Фигура):
    def __init__(self, ширина, высота):
        super().__init__("прямоугольник")
        self.ширина = ширина
        self.высота = высота
    
    def площадь(self):
        return self.ширина * self.высота
    
    def периметр(self):
        return 2 * (self.ширина + self.высота)

class Круг(Фигура):
    def __init__(self, радиус):
        super().__init__("круг")
        self.радиус = радиус
    
    def площадь(self):
        import math
        return math.pi * self.радиус ** 2
    
    def периметр(self):
        import math
        return 2 * math.pi * self.радиус

# Попытка создать экземпляр абстрактного класса вызовет ошибку
# фигура = Фигура("абстрактная фигура")  # TypeError

# Создание экземпляров конкретных классов
прямоугольник = Прямоугольник(5, 3)
круг = Круг(4)

print(прямоугольник.описание())
print(f"Площадь: {прямоугольник.площадь()}")
print(f"Периметр: {прямоугольник.периметр()}")

print(круг.описание())
print(f"Площадь: {круг.площадь():.2f}")
print(f"Периметр: {круг.периметр():.2f}")

Метод разрешения порядка (MRO)

В Python используется алгоритм C3 для определения порядка разрешения методов:

Порядок разрешения методов
class A:
    def метод(self):
        return "Метод из класса A"

class B(A):
    def метод(self):
        return "Метод из класса B"

class C(A):
    def метод(self):
        return "Метод из класса C"

class D(B, C):
    pass

# Проверка порядка разрешения методов
print("MRO для класса D:")
for cls in D.__mro__:
    print(cls)

# Вызов метода
d = D()
print(d.метод())  # Будет вызван метод из класса B

# Явный вызов метода из определенного класса
print(C.метод(d))  # Вызов метода из класса C

Практические примеры

Рассмотрим практические примеры использования наследования:

Система управления сотрудниками
class Сотрудник:
    def __init__(self, имя, фамилия, идентификатор, зарплата):
        self.имя = имя
        self.фамилия = фамилия
        self.идентификатор = идентификатор
        self.зарплата = зарплата
    
    def полное_имя(self):
        return f"{self.имя} {self.фамилия}"
    
    def описание(self):
        return f"Сотрудник: {self.полное_имя()}, ID: {self.идентификатор}"
    
    def рассчитать_зарплату(self):
        return self.зарплата

class Менеджер(Сотрудник):
    def __init__(self, имя, фамилия, идентификатор, зарплата, бонус, отдел):
        super().__init__(имя, фамилия, идентификатор, зарплата)
        self.бонус = бонус
        self.отдел = отдел
    
    def описание(self):
        базовое_описание = super().описание()
        return f"{базовое_описание}, Отдел: {self.отдел}"
    
    def рассчитать_зарплату(self):
        return self.зарплата + self.бонус
    
    def управлять(self):
        return f"{self.полное_имя()} управляет отделом {self.отдел}"

class Разработчик(Сотрудник):
    def __init__(self, имя, фамилия, идентификатор, зарплата, язык_программирования, уровень):
        super().__init__(имя, фамилия, идентификатор, зарплата)
        self.язык_программирования = язык_программирования
        self.уровень = уровень
    
    def описание(self):
        базовое_описание = super().описание()
        return f"{базовое_описание}, Язык: {self.язык_программирования}, Уровень: {self.уровень}"
    
    def программировать(self):
        return f"{self.полное_имя()} программирует на {self.язык_программирования}"
    
    def повысить_уровень(self):
        уровни = ["Junior", "Middle", "Senior"]
        if self.уровень in уровни and уровни.index(self.уровень) < len(уровни) - 1:
            новый_уровень = уровни[уровни.index(self.уровень) + 1]
            self.уровень = новый_уровень
            return f"{self.полное_имя()} повышен до уровня {self.уровень}"
        return f"{self.полное_имя()} уже имеет максимальный уровень"

# Использование системы сотрудников
менеджер = Менеджер("Иван", "Петров", "M001", 80000, 20000, "IT")
разработчик = Разработчик("Анна", "Сидорова", "D001", 60000, "Python", "Middle")

# Вывод информации
print(менеджер.описание())
print(f"Зарплата менеджера: {менеджер.рассчитать_зарплату()}")
print(менеджер.управлять())

print(разработчик.описание())
print(f"Зарплата разработчика: {разработчик.рассчитать_зарплату()}")
print(разработчик.программировать())
print(разработчик.повысить_уровень())

Практические задания

Задания
  1. Создайте иерархию классов "Транспортное средство" → "Автомобиль" → "Электромобиль". Добавьте соответствующие атрибуты и методы для каждого уровня.
  2. Реализуйте систему "Геометрические фигуры" с абстрактным базовым классом и конкретными реализациями (квадрат, треугольник, круг).
  3. Создайте класс "Животное" и несколько подклассов (птица, рыба, млекопитающее) с уникальными методами для каждого типа.
  4. Разработайте систему "Устройства" с базовым классом и подклассами (смартфон, ноутбук, планшет), реализующими общие и специфические функции.
Решения:

Задание 1:

class ТранспортноеСредство:
    def __init__(self, марка, модель, год):
        self.марка = марка
        self.модель = модель
        self.год = год
    
    def описание(self):
        return f"{self.марка} {self.модель} ({self.год})"
    
    def завести(self):
        return "Транспортное средство заведено"

class Автомобиль(ТранспортноеСредство):
    def __init__(self, марка, модель, год, объем_двигателя):
        super().__init__(марка, модель, год)
        self.объем_двигателя = объем_двигателя
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, двигатель {self.объем_двигателя} л"
    
    def ехать(self):
        return f"{self.марка} {self.модель} едет"

class Электромобиль(Автомобиль):
    def __init__(self, марка, модель, год, емкость_батареи):
        super().__init__(марка, модель, год, "электрический")
        self.емкость_батареи = емкость_батареи
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, батарея {self.емкость_батареи} кВт·ч"
    
    def зарядить(self):
        return f"{self.марка} {self.модель} заряжается"
    
    def ехать(self):
        return f"{self.марка} {self.модель} едет тихо на электричестве"

# Пример использования
электромобиль = Электромобиль("Tesla", "Model 3", 2023, 75)
print(электромобиль.описание())
print(электромобиль.завести())
print(электромобиль.ехать())
print(электромобиль.зарядить())

Задание 2:

from abc import ABC, abstractmethod
import math

class Фигура(ABC):
    def __init__(self, название):
        self.название = название
    
    @abstractmethod
    def площадь(self):
        pass
    
    @abstractmethod
    def периметр(self):
        pass

class Квадрат(Фигура):
    def __init__(self, сторона):
        super().__init__("квадрат")
        self.сторона = сторона
    
    def площадь(self):
        return self.сторона ** 2
    
    def периметр(self):
        return 4 * self.сторона

class Треугольник(Фигура):
    def __init__(self, сторона_a, сторона_b, сторона_c):
        super().__init__("треугольник")
        self.сторона_a = сторона_a
        self.сторона_b = сторона_b
        self.сторона_c = сторона_c
    
    def площадь(self):
        # Формула Герона
        p = self.периметр() / 2
        return math.sqrt(p * (p - self.сторона_a) * (p - self.сторона_b) * (p - self.сторона_c))
    
    def периметр(self):
        return self.сторона_a + self.сторона_b + self.сторона_c

class Круг(Фигура):
    def __init__(self, радиус):
        super().__init__("круг")
        self.радиус = радиус
    
    def площадь(self):
        return math.pi * self.радиус ** 2
    
    def периметр(self):
        return 2 * math.pi * self.радиус

# Пример использования
фигуры = [
    Квадрат(5),
    Треугольник(3, 4, 5),
    Круг(3)
]

for фигура in фигуры:
    print(f"{фигура.название.capitalize()}: площадь = {фигура.площадь():.2f}, периметр = {фигура.периметр():.2f}")

Задание 3:

class Животное:
    def __init__(self, имя, возраст):
        self.имя = имя
        self.возраст = возраст
    
    def описание(self):
        return f"{self.имя} - животное, возраст {self.возраст} лет"
    
    def спать(self):
        return f"{self.имя} спит"

class Птица(Животное):
    def __init__(self, имя, возраст, размах_крыльев):
        super().__init__(имя, возраст)
        self.размах_крыльев = размах_крыльев
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, размах крыльев {self.размах_крыльев} см"
    
    def летать(self):
        return f"{self.имя} летает"
    
    def чирикать(self):
        return f"{self.имя} чирикает"

class Рыба(Животное):
    def __init__(self, имя, возраст, глубина_обитания):
        super().__init__(имя, возраст)
        self.глубина_обитания = глубина_обитания
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, глубина обитания {self.глубина_обитания} м"
    
    def плавать(self):
        return f"{self.имя} плавает"
    
    def нырять(self):
        return f"{self.имя} ныряет на глубину {self.глубина_обитания} м"

class Млекопитающее(Животное):
    def __init__(self, имя, возраст, температура_тела):
        super().__init__(имя, возраст)
        self.температура_тела = температура_тела
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, температура тела {self.температура_тела}°C"
    
    def греться(self):
        return f"{self.имя} греется на солнце"
    
    def выкармливать(потомство):
        return f"{self.имя} выкармливает потомство молоком"

# Пример использования
птица = Птица("Синица", 2, 15)
рыба = Рыба("Окунь", 3, 10)
млекопитающее = Млекопитающее("Собака", 5, 38)

животные = [птица, рыба, млекопитающее]

for животное in животные:
    print(животное.описание())
    if isinstance(животное, Птица):
        print(животное.летать())
        print(животное.чирикать())
    elif isinstance(животное, Рыба):
        print(животное.плавать())
        print(животное.нырять())
    elif isinstance(животное, Млекопитающее):
        print(животное.греться())
        print(животное.выкармливать("щенки"))
    print()

Задание 4:

class Устройство:
    def __init__(self, бренд, модель, год_выпуска):
        self.бренд = бренд
        self.модель = модель
        self.год_выпуска = год_выпуска
        self.включено = False
    
    def описание(self):
        return f"{self.бренд} {self.модель} ({self.год_выпуска})"
    
    def включить(self):
        self.включено = True
        return f"{self.бренд} {self.модель} включен"
    
    def выключить(self):
        self.включено = False
        return f"{self.бренд} {self.модель} выключен"

class Смартфон(Устройство):
    def __init__(self, бренд, модель, год_выпуска, диагональ_экрана):
        super().__init__(бренд, модель, год_выпуска)
        self.диагональ_экрана = диагональ_экрана
        self.приложения = []
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, экран {self.диагональ_экрана} дюймов"
    
    def установить_приложение(self, приложение):
        self.приложения.append(приложение)
        return f"Приложение {приложение} установлено"
    
    def позвонить(self, номер):
        if not self.включено:
            return "Сначала включите устройство"
        return f"Звонок на номер {номер}"

class Ноутбук(Устройство):
    def __init__(self, бренд, модель, год_выпуска, объем_памяти):
        super().__init__(бренд, модель, год_выпуска)
        self.объем_памяти = объем_памяти
        self.открытые_программы = []
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, память {self.объем_памяти} ГБ"
    
    def открыть_программу(self, программа):
        self.открытые_программы.append(программа)
        return f"Программа {программа} открыта"
    
    def работать(self):
        if not self.включено:
            return "Сначала включите устройство"
        return f"Работа с программами: {', '.join(self.открытые_программы)}"

class Планшет(Устройство):
    def __init__(self, бренд, модель, год_выпуска, тип_сенсора):
        super().__init__(бренд, модель, год_выпуска)
        self.тип_сенсора = тип_сенсора
        self.заряд = 100
    
    def описание(self):
        базовое = super().описание()
        return f"{базовое}, сенсор {self.тип_сенсора}"
    
    def использовать_сенсор(self):
        if not self.включено:
            return "Сначала включите устройство"
        return f"Используется {self.тип_сенсора} сенсор"
    
    def проверить_заряд(self):
        return f"Уровень заряда: {self.заряд}%"

# Пример использования
смартфон = Смартфон("Apple", "iPhone 14", 2022, 6.1)
ноутбук = Ноутбук("Dell", "XPS 13", 2023, 16)
планшет = Планшет("Samsung", "Galaxy Tab", 2022, "емкостный")

устройства = [смартфон, ноутбук, планшет]

for устройство in устройства:
    print(устройство.описание())
    print(устройство.включить())
    
    if isinstance(устройство, Смартфон):
        print(устройство.установить_приложение("Калькулятор"))
        print(устройство.позвонить("123-456-7890"))
    elif isinstance(устройство, Ноутбук):
        print(устройство.открыть_программу("Visual Studio Code"))
        print(устройство.работать())
    elif isinstance(устройство, Планшет):
        print(устройство.использовать_сенсор())
        print(устройство.проверить_заряд())
    
    print(устройство.выключить())
    print()
Предыдущий урок Следующий урок