Урок 42 Наследование

super(), переопределение методов, полиморфизм

⏱ ~15 мин чтения· 📗 Средний

Что такое наследование?

Наследование позволяет создавать новый класс на основе существующего. Дочерний класс получает все атрибуты и методы родительского и может добавлять свои или изменять существующие.

# Родительский класс
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "???"

# Дочерний класс (наследует от Animal)
class Dog(Animal):
    def speak(self):
        return f"{self.name} говорит: Гав!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} говорит: Мяу!"

dog = Dog("Шарик")
cat = Cat("Мурка")
print(dog.speak())  # Шарик говорит: Гав!
print(cat.speak())  # Мурка говорит: Мяу!

Синтаксис: class Дочерний(Родительский):

super() — вызов родительского метода

super() позволяет вызвать метод родительского класса из дочернего. Особенно важно для __init__:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def info(self):
        return f"{self.name}, {self.age} лет"

class Student(Person):
    def __init__(self, name, age, group):
        super().__init__(name, age)   # вызываем __init__ родителя
        self.group = group

    def info(self):
        base_info = super().info()     # вызываем info() родителя
        return f"{base_info}, группа {self.group}"

student = Student("Анна", 20, "П-101")
print(student.info())  # Анна, 20 лет, группа П-101

Переопределение методов (override)

Если метод дочернего класса имеет то же имя, что и метод родителя, — он переопределяет его:

class Shape:
    def area(self):
        return 0

    def describe(self):
        return f"Фигура с площадью {self.area()}"

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):                   # переопределяем!
        return self.width * self.height

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):                   # переопределяем!
        return 3.14159 * self.radius ** 2

shapes = [Rectangle(10, 5), Circle(7)]
for shape in shapes:
    print(shape.describe())

# Вывод:
# Фигура с площадью 50
# Фигура с площадью 153.93791

Полиморфизм

Полиморфизм («много форм») — возможность использовать объекты разных классов через единый интерфейс. В примере выше rectangle.describe() и circle.describe() работают одинаково, хотя внутри вызывают разные area(). Это и есть полиморфизм.

# Все животные умеют speak(), но каждый по-своему
animals = [Dog("Бобик"), Cat("Мурзик"), Dog("Рекс")]

for animal in animals:
    print(animal.speak())

# Вывод:
# Бобик говорит: Гав!
# Мурзик говорит: Мяу!
# Рекс говорит: Гав!

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

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

class Flyer:
    def fly(self):
        return "Я лечу!"

class Swimmer:
    def swim(self):
        return "Я плыву!"

class Duck(Flyer, Swimmer):
    def speak(self):
        return "Кря!"

duck = Duck()
print(duck.fly())    # Я лечу!
print(duck.swim())   # Я плыву!
print(duck.speak())  # Кря!

💡 Совет: MRO (Method Resolution Order): Python ищет методы слева направо по цепочке наследования.

isinstance() и issubclass()

class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()

print(isinstance(dog, Dog))       # True
print(isinstance(dog, Animal))    # True (т.к. Dog наследует от Animal)
print(isinstance(42, Animal))     # False

print(issubclass(Dog, Animal))    # True
print(issubclass(Animal, Dog))    # False

Практический пример: система сотрудников

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def get_bonus(self):
        return self.salary * 0.10

    def get_total(self):
        return self.salary + self.get_bonus()

class Manager(Employee):
    def get_bonus(self):
        return self.salary * 0.20   # менеджеры получают больше

class Developer(Employee):
    def __init__(self, name, salary, lang):
        super().__init__(name, salary)
        self.lang = lang

    def get_bonus(self):
        return self.salary * 0.15 + 10000  # надбавка за язык

staff = [
    Employee("Иван", 50000),
    Manager("Петя", 80000),
    Developer("Анна", 100000, "Python"),
]

for person in staff:
    print(f"{person.name}: {person.get_total():,.0f} ₽")

# Вывод:
# Иван: 55,000 ₽
# Петя: 96,000 ₽
# Анна: 125,000 ₽