Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает декоратор @classmethod
Декоратор @classmethod превращает метод в метод класса. Вместо получения экземпляра объекта (self), он получает сам класс (cls) в качестве первого аргумента. Это позволяет методу работать с данными и состоянием класса, а не конкретного экземпляра.
Основные различия: @classmethod vs @staticmethod vs обычные методы
class MyClass:
class_variable = "I am a class variable"
# Обычный метод экземпляра
def instance_method(self):
# self — экземпляр класса
return f"Instance method called on {self}"
# Метод класса
@classmethod
def class_method(cls):
# cls — сам класс, не экземпляр
return f"Class method called on {cls.__name__}"
# Статический метод
@staticmethod
def static_method():
# Нет ни self, ни cls
return "Static method called"
# Использование
obj = MyClass()
print(obj.instance_method()) # Instance method called on <__main__.MyClass object>
print(MyClass.class_method()) # Class method called on MyClass
print(MyClass.static_method()) # Static method called
# Интересно: class_method можно вызвать и на экземпляре
print(obj.class_method()) # Class method called on MyClass
Как @classmethod работает внутри
class Demo:
@classmethod
def show(cls):
print(f"cls = {cls}")
print(f"cls.__name__ = {cls.__name__}")
# Когда вызываешь Demo.show(), Python автоматически передаёт сам класс Demo
Demo.show()
# cls = <class "__main__.Demo">
# cls.__name__ = Demo
Типичные применения @classmethod
1. Альтернативные конструкторы (Factory Methods):
from datetime 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 из года рождения"""
age = datetime.now().year - birth_year
return cls(name, age) # Возвращает экземпляр класса
@classmethod
def from_string(cls, person_str):
"""Создаёт объект Person из строки Name:Age"""
name, age = person_str.split(":")
return cls(name, int(age))
# Использование
person1 = Person("Alice", 30)
person2 = Person.from_birth_year("Bob", 1990)
person3 = Person.from_string("Charlie:25")
print(person1.name, person1.age) # Alice 30
print(person2.name, person2.age) # Bob 34 (примерно)
print(person3.name, person3.age) # Charlie 25
2. Работа с переменными класса:
class Counter:
count = 0 # Переменная класса
def __init__(self, name):
self.name = name
Counter.count += 1 # Увеличиваем счётчик класса
@classmethod
def get_count(cls):
"""Возвращает количество созданных объектов"""
return cls.count
@classmethod
def reset_count(cls):
"""Сбрасывает счётчик"""
cls.count = 0
obj1 = Counter("First")
obj2 = Counter("Second")
obj3 = Counter("Third")
print(Counter.get_count()) # 3
Counter.reset_count()
print(Counter.get_count()) # 0
3. Наследование и @classmethod:
class Animal:
@classmethod
def from_name_and_species(cls, name, species):
"""Factory method для создания животных"""
obj = cls()
obj.name = name
obj.species = species
return obj
class Dog(Animal):
pass
class Cat(Animal):
pass
# Обратите внимание: cls правильно указывает на подкласс
dog = Dog.from_name_and_species("Rex", "Canis familiaris")
cat = Cat.from_name_and_species("Whiskers", "Felis catus")
print(type(dog).__name__) # Dog
print(type(cat).__name__) # Cat
4. Конфигурирование класса:
class Database:
_config = {"host": "localhost", "port": 5432}
@classmethod
def configure(cls, host=None, port=None):
"""Настраивает параметры подключения"""
if host:
cls._config["host"] = host
if port:
cls._config["port"] = port
@classmethod
def get_connection_string(cls):
config = cls._config
return f"postgresql://{config["host"]}:{config["port"]}/db"
Database.configure(host="prod.server.com", port=5433)
print(Database.get_connection_string())
# postgresql://prod.server.com:5433/db
@classmethod в цепочке наследования
class Base:
value = "Base"
@classmethod
def get_value(cls):
return cls.value
class Child(Base):
value = "Child"
print(Base.get_value()) # Base
print(Child.get_value()) # Child (cls содержит Child, не Base)
Практический пример: Реестр классов
class Plugin:
_registry = {}
def __init__(self, name):
self.name = name
@classmethod
def register(cls, name, plugin_class):
"""Регистрирует плагин"""
cls._registry[name] = plugin_class
@classmethod
def get(cls, name):
"""Возвращает зарегистрированный плагин"""
return cls._registry.get(name)
@classmethod
def list_all(cls):
"""Выводит все зарегистрированные плагины"""
return list(cls._registry.keys())
class AudioPlugin(Plugin):
pass
class VideoPlugin(Plugin):
pass
# Регистрация плагинов
Plugin.register("audio", AudioPlugin)
Plugin.register("video", VideoPlugin)
print(Plugin.list_all()) # [audio, video]
print(Plugin.get("audio")) # <class AudioPlugin>
Сравнение @classmethod и @staticmethod
class Example:
name = "Example"
@classmethod
def class_method(cls):
# Может получить доступ к переменным класса
return f"Class method: {cls.name}"
@staticmethod
def static_method():
# НЕ может получить доступ к переменным класса
return "Static method: no access to class"
print(Example.class_method()) # Class method: Example
print(Example.static_method()) # Static method: no access to class
Лучшие практики
- Используй @classmethod для factory methods — альтернативных конструкторов
- Используй @classmethod для работы с переменными класса — когда нужно изменить состояние класса
- Вместо явного вызова cls() — всегда используй cls() в factory методах для поддержки наследования
- Документируй factory methods — явно указывай, что это альтернативный конструктор
- Используй @staticmethod, если не нужен доступ к классу — это проще и понятнее
@classmethod — мощный инструмент для создания более гибких и переиспользуемых классов, особенно в контексте наследования и factory patterns.