Ответ
Декоратор @classmethod в Python преобразует метод так, что он получает класс в качестве первого аргумента, а не экземпляр объекта. По соглашению, этот аргумент называется cls.
Это позволяет методу работать с атрибутами самого класса, а не с состоянием конкретного экземпляра. В отличие от обычных методов, которые требуют self, @classmethod можно вызывать напрямую от класса, не создавая его экземпляр.
Ключевые отличия от других типов методов:
- Обычный метод:
def my_method(self, ...)— получает экземпляр как первый аргумент. - Метод класса:
def my_method(cls, ...)— получает класс как первый аргумент. - Статический метод (
@staticmethod):def my_method(...)— не получает ни экземпляр, ни класс и ведет себя как обычная функция, находящаяся в пространстве имен класса.
Основное применение — фабричные методы, которые предоставляют альтернативные способы создания экземпляров класса.
Пример 1: Работа с атрибутами класса
class Product:
# Атрибут класса, общий для всех экземпляров
total_products = 0
def __init__(self, name):
self.name = name
Product.increment_total()
@classmethod
def increment_total(cls):
# cls ссылается на класс Product
cls.total_products += 1
@classmethod
def get_total_products(cls):
return cls.total_products
p1 = Product("Phone")
p2 = Product("Laptop")
# Вызов метода через класс
print(f"Всего продуктов: {Product.get_total_products()}") # Вывод: Всего продуктов: 2
Пример 2: Фабричный метод
import datetime
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
"""Создает экземпляр Person, вычисляя возраст по году рождения."""
current_year = datetime.date.today().year
age = current_year - birth_year
# cls(name, age) эквивалентно Person(name, age)
return cls(name, age)
# Создание объекта через стандартный конструктор
person1 = Person("Alice", 34)
# Создание объекта через фабричный метод
person2 = Person.from_birth_year("Bob", 1990)
print(f"{person2.name} is {person2.age} years old.")
# Вывод (в 2024 году): Bob is 34 years old.