Что представляет собой параметр `cls` в методе, декорированном `@classmethod` в Python

Ответ

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

Это позволяет методу работать с атрибутами класса или создавать экземпляры класса, не имея доступа к конкретному объекту. Основное применение — фабричные методы, которые предоставляют альтернативные способы создания объектов.

Пример: Фабричный метод

Предположим, мы хотим создавать объект User из строки с датой рождения в формате YYYY-MM-DD.

from datetime import date

class User:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    @classmethod
    def from_birth_year(cls, name: str, birth_year: int):
        """Альтернативный конструктор для создания пользователя по году рождения."""
        current_year = date.today().year
        age = current_year - birth_year
        # cls() вызывает конструктор __init__ класса User
        return cls(name, age)

# Основной способ создания
user1 = User("Alice", 30)

# Использование фабричного метода @classmethod
user2 = User.from_birth_year("Bob", 1995)

print(f"{user2.name} is {user2.age} years old.")
# Вывод: Bob is 29 years old. (для 2024 года)

Ключевые отличия от self:

  • cls: Ссылается на класс (User). Используется в @classmethod.
  • self: Ссылается на конкретный экземпляр класса (user1, user2). Используется в обычных методах экземпляра.

Ответ 18+ 🔞

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

Вот смотри, чтобы не быть мудаком, который везде пихает self, когда надо работать со всем классом, придумали эту фишку. cls — это сам класс, целиком, в его первозданной красоте. Не какой-то там отдельный экземпляр, а вся сущность, блядь, целиком.

Основная фишка — это фабричные методы. Ну, то есть альтернативные способы слепить объект, когда стандартный __init__ тебе неудобен, как сиденье унитаза после соседа-слона.

Допустим, у тебя класс User. И ты хочешь его создавать не по возрасту, а по году рождения. Ну, логично же, ёпта! Зачем считать вручную, если можно заставить класс сделать это за тебя?

from datetime import date

class User:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    @classmethod
    def from_birth_year(cls, name: str, birth_year: int):
        """Вот тут мы и колдуем. Делаем пользователя из года рождения."""
        current_year = date.today().year
        age = current_year - birth_year
        # А вот тут самое важное! cls() — это волшебный пендель,
        # который говорит: "Эй, класс, из которого меня вызвали, создай сам себя с этими параметрами!"
        return cls(name, age)

# Стандартный способ, скучный, но надёжный
user1 = User("Алиса", 30)

# А вот это уже круто! Вызываем метод КЛАССА, а не объекта.
# Словно говорим: "User, будь добр, роди мне Боба из 1995 года".
user2 = User.from_birth_year("Боб", 1995)

print(f"{user2.name} is {user2.age} years old.")
# Выведет что-то вроде: Bob is 29 years old. (если на дворе 2024-й, конечно)

И главное, блядь, понимать разницу, а то потом сидишь и думаешь, почему всё ебёт мозг:

  • self — это ты сам, конкретный, потный, со своими косяками и атрибутами. Это твой личный экземпляр. Обычные методы с ним и работают.
  • cls — это, блин, как бы твой паспорт, твоя родословная. Это сам класс User. И метод с @classmethod работает не с одной тушкой, а со всей концепцией в целом. Он может новых "тебя" создавать, в обход стандартного входа.

Короче, cls — это когда нужно действовать от имени всей фамилии, а не от имени отдельного её члена. Понял, да? А то опять будешь self туда пихать, где не надо, и удивляться, почему всё падает с ошибкой, как ядрёна вошь.