Пример 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
Как ищется: D → B → A. Метод найден, поиск останавливается. До 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 для D — D → 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
- Создайте папку
python-fundamentals-practice/lesson-72/. - Создайте файл
multiple_inheritance.pyи скопируйте в него любой пример. - Откройте терминал в VS Code (Ctrl + `).
- Активируйте виртуальное окружение:
venv\Scripts\activate(Windows) илиsource venv/bin/activate(Mac/Linux). - Запустите:
python multiple_inheritance.py