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

⚡ Минимальный рабочий пример

class AuthMixin:
    def login(self) -> None:
        if not hasattr(self, "username"):
            raise AttributeError("Не задан username")
        print(f"{self.username} вошёл.")


class User(AuthMixin):
    def __init__(self, username: str) -> None:
        self.username = username


user = User("alice")
user.login()  # alice вошёл.

Пример 1. Базовое множественное наследование

Класс Report получает методы сразу от двух родителей: Printable и Savable.

class Printable:
    def print_info(self) -> None:
        print("Печать информации...")


class Savable:
    def save(self) -> None:
        print("Сохраняем в файл...")


class Report(Printable, Savable):
    pass


report = Report()
report.print_info()  # Печать информации...
report.save()        # Сохраняем в файл...

Что происходит: Report не определяет собственных методов, но наследует их от обоих родителей. Порядок родителей важен, если имена методов совпадают.

Пример 2. Проверка атрибутов через hasattr

hasattr позволяет безопасно проверить, есть ли у объекта нужный атрибут, прежде чем обращаться к нему.

class User:
    def __init__(self, username: str) -> None:
        self.username = username


user = User("Alice")
print(hasattr(user, "username"))  # True
print(hasattr(user, "email"))     # False

if hasattr(user, "email"):
    print(user.email)
else:
    print("Поле email отсутствует")

Пример 3. Миксины для аутентификации и уведомлений

Миксины добавляют поведение и проверяют через hasattr, что у наследника есть нужные поля.

class AuthMixin:
    def login(self) -> None:
        if not hasattr(self, "username"):
            raise AttributeError("Не задан username")
        print(f"{self.username} вошёл в систему.")

    def logout(self) -> None:
        print("Пользователь вышел из системы.")


class NotificationMixin:
    def send_email(self, message: str) -> None:
        if not hasattr(self, "email"):
            raise AttributeError("Не задан email")
        print(f"Отправка письма на {self.email}: {message}")


class UserProfile(AuthMixin, NotificationMixin):
    def __init__(self, username: str, email: str) -> None:
        self.username = username
        self.email = email


user = UserProfile("alice", "alice@example.com")
user.login()
user.send_email("Добро пожаловать!")
user.logout()

Пример 4. Порядок поиска методов

Порядок родителей влияет на результат. Здесь D(B, C) находит greet в A, потому что поиск идёт через B вглубь.

class A:
    def greet(self) -> None:
        print("Hello from A")


class B(A):
    pass


class C:
    def greet(self) -> None:
        print("Hello from C")


class D(B, C):
    pass


d = D()
d.greet()  # Hello from A

Как ищется: DBA. Метод найден, поиск останавливается. До C Python не доходит.

Пример 5. Просмотр MRO через __mro__

Атрибут __mro__ показывает точный порядок разрешения методов. Полезен для отладки.

class A:
    def greet(self) -> None:
        print("Hello from A")


class B(A):
    def greet(self) -> None:
        print("Hello from B")


class C(A):
    def greet(self) -> None:
        print("Hello from C")


class D(B, C):
    def greet(self) -> None:
        print("Hello from D")


print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>,
#  <class '__main__.C'>, <class '__main__.A'>,
#  <class 'object'>)

Пример 6. super() в множественном наследовании

super() следует по MRO и вызывает каждую реализацию метода action в цепочке.

class A:
    def action(self) -> None:
        print("A")


class B(A):
    def action(self) -> None:
        print("B")
        super().action()


class C(A):
    def action(self) -> None:
        print("C")
        super().action()


class D(B, C):
    def action(self) -> None:
        print("D")
        super().action()


d = D()
d.action()
# D
# B
# C
# A

Что происходит: MRO для DD → B → C → A → object. Каждый super().action() передаёт управление следующему классу в этом порядке.

Пример 7. Композиция

Menu создаёт кнопку выхода внутри себя. Кнопка не существует отдельно от меню.

class ExitButton:
    def click(self) -> None:
        print("Выход из программы")


class Menu:
    def __init__(self) -> None:
        self.exit_button = ExitButton()  # композиция

    def show(self) -> None:
        print("Меню открыто")
        self.exit_button.click()


menu = Menu()
menu.show()
# Меню открыто
# Выход из программы

Пример 8. Агрегация

Teacher использует University, но университет создаётся снаружи и может использоваться несколькими преподавателями.

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

    def get_info(self) -> None:
        print(f"Обучение проходит в университете: {self.name}")


class Teacher:
    def __init__(self, name: str, university: University) -> None:
        self.name = name
        self.university = university  # агрегация

    def introduce(self) -> None:
        print(f"Преподаватель: {self.name}")
        self.university.get_info()


uni = University("Tech University")
t1 = Teacher("Anna", uni)
t2 = Teacher("Dmitry", uni)
t1.introduce()
t2.introduce()

Пример 9. Вложенный класс как композиция

Батарея логически является частью смартфона и не имеет смысла без него.

class Smartphone:
    class Battery:
        def __init__(self, capacity: int) -> None:
            self.capacity = capacity
            self.charge = capacity

        def use(self, amount: int) -> None:
            self.charge = max(self.charge - amount, 0)
            print(f"Батарея: {self.charge}/{self.capacity} мАч")

    def __init__(self, model: str, battery_capacity: int) -> None:
        self.model = model
        self.battery = self.Battery(battery_capacity)

    def play_video(self) -> None:
        print(f"{self.model} воспроизводит видео...")
        self.battery.use(300)


phone = Smartphone("Pixel 9", 4000)
phone.play_video()

Как запустить в VS Code

  1. Создайте папку python-fundamentals-practice/lesson-72/.
  2. Создайте файл multiple_inheritance.py и скопируйте в него любой пример.
  3. Откройте терминал в VS Code (Ctrl + `).
  4. Активируйте виртуальное окружение: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  5. Запустите: python multiple_inheritance.py
Проверить по документации: примеры дополнены аннотациями типов, которых не было в исходном материале. Подробнее о типах см. typing и PEP 484.