Как работает наследование в Python?

«Как работает наследование в Python?» — вопрос из категории Скриптинг и автоматизация, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В DevOps я часто использую Python для написания скриптов автоматизации, мониторинга и управления инфраструктурой. Наследование в Python позволяет мне создавать гибкие и переиспользуемые классы для таких задач.

Основные принципы:

  • Одиночное наследование: Класс может наследовать от одного родительского класса.
  • Множественное наследование: Класс может наследовать от нескольких родительских классов. Порядок разрешения методов (MRO) определяется алгоритмом C3.
  • Переопределение методов: Дочерний класс может переопределять методы родителя.
  • Расширение функциональности: С помощью super() можно вызывать метод родительского класса для его расширения.

Практический пример из DevOps: Я создаю базовый класс для обработчика логов, а затем специализированные классы для разных систем.

class BaseLogHandler:
    """Базовый класс для обработки логов"""
    def __init__(self, log_source):
        self.log_source = log_source

    def parse(self, raw_log):
        """Базовая логика парсинга"""
        return {"source": self.log_source, "raw": raw_log}

class CloudWatchLogHandler(BaseLogHandler):
    """Обработчик для AWS CloudWatch Logs"""
    def __init__(self, log_group):
        # Вызываем конструктор родителя
        super().__init__(log_source="aws_cloudwatch")
        self.log_group = log_group

    def parse(self, raw_log):
        """Переопределяем парсинг для специфичного формата CloudWatch"""
        base_data = super().parse(raw_log)  # Используем базовую логику
        # Добавляем CloudWatch-специфичные поля
        base_data["log_group"] = self.log_group
        base_data["timestamp"] = self._extract_cw_timestamp(raw_log)
        return base_data

    def _extract_cw_timestamp(self, log):
        """Внутренний метод для извлечения временной метки"""
        # Логика парсинга timestamp из CloudWatch
        return log.get("@timestamp", "unknown")

# Использование
handler = CloudWatchLogHandler("/aws/lambda/my-function")
parsed = handler.parse({"@timestamp": "2024-01-15T10:30:00Z", "message": "Lambda started"})
print(parsed)

Проверка отношений:

print(issubclass(CloudWatchLogHandler, BaseLogHandler))  # True
print(isinstance(handler, BaseLogHandler))               # True

Зачем это в DevOps: Такой подход позволяет мне:

  1. Создавать единый интерфейс для работы с разными системами логирования (CloudWatch, ELK, Loki).
  2. Легко добавлять поддержку новых систем, наследуясь от базового класса.
  3. Избегать дублирования кода для общей логики (например, отправки алертов).
  4. Строить сложные пайплайны обработки данных, где разные обработчики могут использоваться полиморфно.