📖 Теория: summary session 18

⚡ Кратко

Наследование даёт повторное использование кода. Полиморфизм — один вызов, разное поведение. super() вызывает метод ближайшего родителя. isinstance/issubclass проверяют типы с учётом иерархии.

  • Наследование: class Child(Parent):.
  • Переопределение: дочерний класс заменяет метод родителя.
  • super(): super().__init__(...) и super().method().
  • isinstance(obj, Class): True для Class и его наследников.
  • issubclass(A, B): True, если A наследуется от B.

4 принципа ООП

ПринципСуть
Наследование Один класс получает поля и методы другого. Позволяет избежать дублирования и расширять логику.
Инкапсуляция Объект скрывает внутреннее устройство и предоставляет только нужный интерфейс.
Полиморфизм Один интерфейс может вести себя по-разному в зависимости от конкретного типа объекта.
Абстракция Выделение общих характеристик и поведения, чтобы задать интерфейс для будущих реализаций.

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

Базовый (родительский, супер) класс — тот, от которого наследуются. Дочерний (производный, подкласс) класс — тот, который наследует.

class Employee:
    def __init__(self, name: str) -> None:
        self.name = name

    def work(self) -> None:
        print(f"{self.name} is working...")


class Programmer(Employee):
    pass


class Manager(Employee):
    pass


programmer = Programmer("Alice")
manager = Manager("Bob")
programmer.work()  # Alice is working...
manager.work()     # Bob is working...
💡 На заметку: дочерний класс наследует все поля и методы родителя, но может добавлять свои и переопределять унаследованное поведение.

Полиморфизм

Полиморфизм (от греч. «много форм») позволяет использовать один и тот же интерфейс для разных типов объектов. В Python основной механизм — переопределение методов (overriding).

class Employee:
    def __init__(self, name: str) -> None:
        self.name = name

    def work(self) -> None:
        print(f"{self.name} is working...")


class Programmer(Employee):
    def work(self) -> None:
        print(f"{self.name} is writing code...")


class Manager(Employee):
    def work(self) -> None:
        print(f"{self.name} is managing team...")


staff = [Programmer("Alice"), Manager("Bob"), Programmer("Bill")]
for person in staff:
    person.work()

Python автоматически выбирает нужную версию метода в зависимости от реального класса объекта.

⚠️ Важно: Python не поддерживает перегрузку методов по количеству аргументов. Если определить метод дважды, сохранится только последнее определение.

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

Если дочерний класс заново определяет метод родителя, при вызове через объект дочернего класса используется новая версия. Это и есть overriding.

Функция super()

super() вызывает метод ближайшего родителя с учётом порядка разрешения методов (MRO). Она помогает не дублировать общий код.

super() в __init__

class Employee:
    def __init__(self, name: str) -> None:
        self.name = name


class Programmer(Employee):
    def __init__(self, name: str, language: str) -> None:
        super().__init__(name)
        self.language = language


p = Programmer("Alice", "Python")
print(p.name)      # Alice
print(p.language)  # Python

super() в обычном методе

class Employee:
    def work(self) -> None:
        print("General tasks")


class Programmer(Employee):
    def work(self) -> None:
        super().work()
        print("Writing code")


Programmer().work()
# General tasks
# Writing code

super() в цепочке наследования

class Person:
    def __init__(self, name: str) -> None:
        self.name = name
        print(f"Init Person: {self.name}")


class Employee(Person):
    def work(self) -> None:
        print(f"{self.name} is working...")


class Manager(Employee):
    def __init__(self, name: str, department: str) -> None:
        super().__init__(name)
        self.department = department
        print(f"Init Manager: {self.name} manages {self.department}")


m = Manager("Alice", "Development")
# Init Person: Alice
# Init Manager: Alice manages Development
💡 На заметку: используйте super() вместо явного вызова Parent.__init__(self): так код не привязан к имени родителя и корректно работает при изменении иерархии.

isinstance()

Проверяет, является ли объект экземпляром указанного класса или его наследника. Работает и со встроенными типами, и с кортежем классов.

class Employee:
    pass


class Programmer(Employee):
    pass


p = Programmer()
print(isinstance(p, Programmer))  # True
print(isinstance(p, Employee))    # True — наследник
print(isinstance(p, object))      # True — все классы от object
print(isinstance(p, (str, int)))  # False

issubclass()

Проверяет, является ли один класс подклассом другого — напрямую или через цепочку наследования.

class Employee:
    pass


class Programmer(Employee):
    pass


class BackendDeveloper(Programmer):
    pass


print(issubclass(Programmer, Employee))           # True
print(issubclass(BackendDeveloper, Employee))     # True (через цепочку)
print(issubclass(Employee, Programmer))           # False
print(issubclass(Employee, object))               # True
Проверить по документации: про наследование и полиморфизм см. Python Tutorial — Inheritance, про super()Built-in Functions.