← Назад к вопросам

Зачем нужен @classmethod в Python?

1.7 Middle🔥 131 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

@classmethod декоратор в Python

@classmethod — это встроенный декоратор, который превращает метод в метод класса. Вместо self (экземпляр), получает cls (класс).

Основная различие: instance vs class method

class Person:
    species = \"Homo sapiens\"  # Переменная класса
    
    def __init__(self, name):
        self.name = name  # Переменная экземпляра
    
    # Обычный метод экземпляра
    def greet(self):
        return f\"Hello, I'm {self.name}\"
    
    # Метод класса
    @classmethod
    def get_species(cls):
        return cls.species
    
    # Статический метод (не требует ни self, ни cls)
    @staticmethod
    def is_human():
        return True

# Экземплярный метод
person = Person(\"Alice\")
print(person.greet())  # Hello, I'm Alice

# Методы класса и статический вызываются от класса
print(Person.get_species())  # Homo sapiens
print(Person.is_human())  # True

# Но также можно вызвать от экземпляра
print(person.get_species())  # Homo sapiens

Зачем нужны classmethod

1. Альтернативные конструкторы (factory methods)

Это основное применение. Classmethod позволяет создавать объекты по-разному.

from datetime import datetime

class Date:
    def __init__(self, day, month, year):
        self.day = day
        self.month = month
        self.year = year
    
    def __repr__(self):
        return f\"Date({self.day}/{self.month}/{self.year})\"
    
    # Альтернативный конструктор из строки
    @classmethod
    def from_string(cls, date_string):
        \"\"\"Создаёт Date из строки 'DD/MM/YYYY'\"\"\"
        day, month, year = map(int, date_string.split('/'))
        return cls(day, month, year)
    
    # Альтернативный конструктор из timestamp
    @classmethod
    def from_timestamp(cls, timestamp):
        dt = datetime.fromtimestamp(timestamp)
        return cls(dt.day, dt.month, dt.year)
    
    # Текущая дата
    @classmethod
    def today(cls):
        today = datetime.now()
        return cls(today.day, today.month, today.year)

# Разные способы создания
date1 = Date(15, 3, 2024)
date2 = Date.from_string(\"15/03/2024\")
date3 = Date.today()

print(date1)  # Date(15/3/2024)
print(date2)  # Date(15/3/2024)
print(date3)  # Date(22/3/2026)

2. Работа с наследованием

Classmethod автоматически получит правильный класс при наследовании.

class Animal:
    count = 0
    
    def __init__(self, name):
        self.name = name
        Animal.count += 1
    
    @classmethod
    def get_count(cls):
        \"\"\"Возвращает количество объектов текущего класса\"\"\"
        return cls.count

class Dog(Animal):
    count = 0  # Свой счётчик для собак
    
    def __init__(self, name, breed):
        self.breed = breed
        super().__init__(name)
        Dog.count += 1

class Cat(Animal):
    count = 0  # Свой счётчик для кошек
    
    def __init__(self, name, color):
        self.color = color
        super().__init__(name)
        Cat.count += 1

# Classmethod автоматически использует правильный класс
dog1 = Dog(\"Rex\", \"Labrador\")
dog2 = Dog(\"Max\", \"Bulldog\")
cat1 = Cat(\"Whiskers\", \"Orange\")

print(Dog.get_count())  # 2 (из Dog.count)
print(Cat.get_count())  # 1 (из Cat.count)
print(Animal.get_count())  # 0 (из Animal.count)

3. Доступ к переменным класса

Classmethod может читать и изменять переменные класса.

class BankAccount:
    interest_rate = 0.05  # Переменная класса
    
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
    
    @classmethod
    def set_interest_rate(cls, rate):
        \"\"\"Изменяет процентную ставку для всех счётов\"\"\"
        cls.interest_rate = rate
    
    @classmethod
    def get_interest_rate(cls):
        return cls.interest_rate
    
    def apply_interest(self):
        \"\"\"Применяет процент к балансу\"\"\"
        self.balance *= (1 + self.interest_rate)

# Изменяем ставку для всех счётов
BankAccount.set_interest_rate(0.07)

account1 = BankAccount(\"Alice\", 1000)
print(account1.interest_rate)  # 0.07

account1.apply_interest()
print(account1.balance)  # 1070.0

4. Реальный пример: JSON сериализация

import json
from datetime import datetime

class Person:
    def __init__(self, name, age, birth_date=None):
        self.name = name
        self.age = age
        self.birth_date = birth_date
    
    def to_dict(self):
        return {
            'name': self.name,
            'age': self.age,
            'birth_date': self.birth_date.isoformat() if self.birth_date else None
        }
    
    def to_json(self):
        return json.dumps(self.to_dict())
    
    @classmethod
    def from_dict(cls, data):
        return cls(
            name=data['name'],
            age=data['age'],
            birth_date=datetime.fromisoformat(data['birth_date']) if data.get('birth_date') else None
        )
    
    @classmethod
    def from_json(cls, json_string):
        data = json.loads(json_string)
        return cls.from_dict(data)

# Сериализация
person = Person(\"Bob\", 30, datetime(1994, 5, 15))
json_str = person.to_json()
print(json_str)  # {\"name\": \"Bob\", \"age\": 30, \"birth_date\": \"1994-05-15T00:00:00\"}

# Десериализация
person2 = Person.from_json(json_str)
print(person2.name)  # Bob
print(person2.age)  # 30

5. Правильная работа с наследованием (super с classmethod)

class Base:
    @classmethod
    def create(cls):
        print(f\"Creating {cls.__name__}\")
        return cls()

class Derived(Base):
    def __init__(self):
        self.value = 42

obj = Derived.create()  # Creating Derived
print(obj.value)  # 42

classmethod vs staticmethod vs обычный метод

ТипПолучаетИспользование
Обычный методself (экземпляр)Работа с экземпляром
@classmethodcls (класс)Альтернативные конструкторы, работа с классом
@staticmethodничегоУтилиты, не связанные с классом
class Example:
    class_var = \"class\" 
    
    def __init__(self, value):
        self.instance_var = value
    
    def instance_method(self):
        return f\"Instance: {self.instance_var}\"
    
    @classmethod
    def class_method(cls):
        return f\"Class: {cls.class_var}\"
    
    @staticmethod
    def static_method():
        return \"Static: no access to instance or class\"

example = Example(\"test\")
print(example.instance_method())  # Instance: test
print(example.class_method())  # Class: class
print(example.static_method())  # Static: no access to instance or class

Практические советы

  1. Используй @classmethod для factory methods — создание объектов по-разному
  2. Используй @classmethod при наследовании — автоматически выберет правильный класс
  3. Используй @classmethod для работы с переменными класса — счётчики, глобальные настройки
  4. Используй @staticmethod для утилит — функции, не связанные с экземпляром или классом
  5. Используй обычный метод для работы с экземпляром — по умолчанию

@classmethod — это мощный инструмент для создания гибких и масштабируемых классов.

Зачем нужен @classmethod в Python? | PrepBro