Что такое декоратор @classmethod в Python и как он используется в скриптах для автоматизации?

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

Ответ

@classmethod — это декоратор в Python, который определяет метод класса. Первым аргументом такого метода является сам класс (по соглашению cls), а не экземпляр (self). Это позволяет работать с атрибутами уровня класса и создавать альтернативные конструкторы, что полезно для организации кода в утилитах и скриптах автоматизации.

Основные отличия от обычного метода (self) и статического метода (@staticmethod):

Метод Первый аргумент Может обращаться к атрибутам класса? Может обращаться к атрибутам экземпляра?
Обычный self (экземпляр) Да (через self.__class__) Да
@classmethod cls (класс) Да Нет (без экземпляра)
@staticmethod Нет Нет Нет

Практические примеры использования в DevOps-скриптах:

  1. Альтернативный конструктор для создания объектов из конфигурации (YAML/JSON):

    class ServerConfig:
        def __init__(self, host, port, user):
            self.host = host
            self.port = port
            self.user = user
    
        @classmethod
        def from_yaml(cls, filepath):
            """Создает экземпляр ServerConfig из YAML-файла."""
            import yaml
            with open(filepath, 'r') as f:
                data = yaml.safe_load(f)
            # Здесь может быть валидация данных
            return cls(data['host'], data['port'], data['user'])
    
    # Использование
    config = ServerConfig.from_yaml('server_config.yaml')
    print(f"Подключаемся к {config.host}:{config.port}")
  2. Управление общим состоянием или кэшем на уровне класса:

    class DeploymentRegistry:
        _deployed_services = set()  # Атрибут класса, общий для всех экземпляров
    
        def __init__(self, service_name):
            self.service_name = service_name
    
        def deploy(self):
            # ... логика деплоя ...
            self.__class__.add_to_registry(self.service_name)
    
        @classmethod
        def add_to_registry(cls, service):
            cls._deployed_services.add(service)
    
        @classmethod
        def get_all_deployed(cls):
            return list(cls._deployed_services)
    
    # В разных частях скрипта
    deploy1 = DeploymentRegistry('api-service')
    deploy2 = DeploymentRegistry('frontend-service')
    deploy1.deploy()
    deploy2.deploy()
    print(DeploymentRegistry.get_all_deployed())  # ['api-service', 'frontend-service']
  3. Фабричный метод для создания объектов в зависимости от окружения:

    class CloudStorage:
        def __init__(self, bucket):
            self.bucket = bucket
    
        def upload(self, file):
            raise NotImplementedError
    
        @classmethod
        def for_environment(cls, env):
            """Возвращает правильную реализацию хранилища для окружения."""
            if env == "aws":
                return S3Storage()
            elif env == "gcp":
                return GCSStorage()
            else:
                return LocalStorage()
    
    class S3Storage(CloudStorage):
        def upload(self, file):
            print(f"Uploading {file} to AWS S3 bucket {self.bucket}")
    
    # Использование в скрипте деплоя
    storage = CloudStorage.for_environment(os.getenv('CLOUD_PROVIDER', 'aws'))
    storage.upload('backup.tar.gz')

Использование @classmethod делает код скриптов более модульным, тестируемым и удобным для расширения.