← Назад к вопросам
Что передается в параметре cls в декораторе @classmethod?
1.8 Middle🔥 91 комментариев
#Python Core#Soft Skills#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Параметр cls в @classmethod
Прямой ответ
В параметр cls передаётся сам класс, а не экземпляр. Это похоже на self в обычных методах, но вместо экземпляра передаётся класс.
Базовый пример
class Person:
def __init__(self, name):
self.name = name
# Обычный метод — получает экземпляр
def greet(self):
print(f"Hello, I am {self.name}")
# Класс-метод — получает класс
@classmethod
def create_from_string(cls, name_string):
name = name_string.strip().title()
return cls(name) # Вызываем конструктор класса
# Использование
p1 = Person("alice")
p1.greet() # "Hello, I am alice"
# Класс-метод
p2 = Person.create_from_string(" bob ")
p2.greet() # "Hello, I am Bob"
Ключевое отличие:
class Demo:
def instance_method(self):
print(type(self)) # <class '__main__.Demo'> — это экземпляр
print(self.__class__) # <class '__main__.Demo'>
@classmethod
def class_method(cls):
print(cls) # <class '__main__.Demo'> — это сам класс
print(type(cls)) # <class 'type'> — класс это объект типа
d = Demo()
d.instance_method() # self — это экземпляр d
Demo.class_method() # cls — это класс Demo
Почему это полезно
1. Alternative constructors (альтернативные конструкторы)
from datetime import datetime
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
# Стандартный конструктор из трёх чисел
# Date(2024, 3, 23)
# Альтернативный конструктор из строки
@classmethod
def from_string(cls, date_string):
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
# Альтернативный конструктор из timestamp
@classmethod
def from_timestamp(cls, timestamp):
dt = datetime.fromtimestamp(timestamp)
return cls(dt.year, dt.month, dt.day)
def __repr__(self):
return f"Date({self.year}, {self.month}, {self.day})"
# Используем
d1 = Date(2024, 3, 23) # Стандартный
d2 = Date.from_string("2024-03-23") # Из строки
d3 = Date.from_timestamp(1711188000) # Из timestamp
print(d1) # Date(2024, 3, 23)
print(d2) # Date(2024, 3, 23)
print(d3) # Date(2024, 3, 23)
2. Работа с наследованием
class Animal:
def __init__(self, name):
self.name = name
@classmethod
def create_dummy(cls):
# cls — это конкретный класс (Animal, Dog, Cat)
return cls("Unknown")
class Dog(Animal):
def bark(self):
print(f"{self.name} says: Woof!")
class Cat(Animal):
def meow(self):
print(f"{self.name} says: Meow!")
# create_dummy вернёт правильный класс
dog = Dog.create_dummy()
print(type(dog)) # <class '__main__.Dog'>
dog.bark() # "Unknown says: Woof!"
cat = Cat.create_dummy()
print(type(cat)) # <class '__main__.Cat'>
cat.meow() # "Unknown says: Meow!"
# БЕЗ @classmethod приходилось бы писать отдельный метод для каждого класса
3. Singleton паттерн
class Database:
_instance = None
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls() # Создаём один раз
return cls._instance
db1 = Database.get_instance()
db2 = Database.get_instance()
print(db1 is db2) # True — один и тот же объект
4. Счётчик экземпляров класса
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
c1 = Counter("first")
c2 = Counter("second")
c3 = Counter("third")
print(Counter.get_count()) # 3
Counter.reset_count()
print(Counter.get_count()) # 0
Отличие от staticmethod
class Math:
# @staticmethod — не получает ничего
@staticmethod
def add(a, b):
return a + b
# @classmethod — получает класс
@classmethod
def multiply_by_two(cls, a):
# cls здесь не используется, но доступен
return a * 2
print(Math.add(2, 3)) # 5
print(Math.multiply_by_two(4)) # 8
# Разница видна при наследовании
class AdvancedMath(Math):
@staticmethod
def add(a, b):
return a + b + 100 # Переопределяем
@classmethod
def multiply_by_two(cls, a):
# cls.name даст доступ к конкретному классу
return a * 2 + (1 if cls.__name__ == "AdvancedMath" else 0)
print(AdvancedMath.add(2, 3)) # 105 — переопределение работает
print(AdvancedMath.multiply_by_two(4)) # 9
Реальный пример: ORM модель
class BaseModel:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
@classmethod
def all(cls):
"""Получить все объекты из БД"""
# cls — это конкретная модель (User, Post и т.д.)
return database.query(cls).all()
@classmethod
def find_by_id(cls, id):
"""Найти объект по ID"""
return database.query(cls).filter(id=id).first()
@classmethod
def create(cls, **kwargs):
"""Создать и сохранить объект"""
obj = cls(**kwargs)
database.save(obj)
return obj
class User(BaseModel):
pass
class Post(BaseModel):
pass
# Используем
users = User.all() # SELECT * FROM users
user = User.find_by_id(1) # SELECT * FROM users WHERE id = 1
new_user = User.create(name="Alice", email="alice@example.com") # INSERT
posts = Post.all() # SELECT * FROM posts
post = Post.find_by_id(1) # SELECT * FROM posts WHERE id = 1
# Без @classmethod пришлось бы передавать класс как параметр:
# User.all(User) — некрасиво
Как работает мотанно?
class Example:
name = "Example"
@classmethod
def show(cls):
print(f"Class name: {cls.__name__}")
print(f"Class itself: {cls}")
print(f"name attribute: {cls.name}")
Example.show()
# Output:
# Class name: Example
# Class itself: <class '__main__.Example'>
# name attribute: Example
# При вызове: Example.show()
# Python автоматически передаёт Example в параметр cls
# Это как: Example.show(Example)
Практическая подсказка
Используй @classmethod когда:
- Нужен альтернативный конструктор (from_string, from_dict и т.д.)
- Работаешь с переменными класса
- Нужно гарантировать, что вернётся правильный тип при наследовании
- Реализуешь паттерны (singleton, factory)
Не используй @classmethod когда:
- Нужен простой утилитарный метод без привязки к классу (@staticmethod)
- Нужен доступ к экземпляру (обычный метод)
Заключение
В параметр cls передаётся сам класс, а не экземпляр. Это позволяет:
- Создавать альтернативные конструкторы
- Работать с переменными класса
- Правильно работать с наследованием
- Писать фабрики и паттерны
Память:
self= текущий экземплярcls= текущий класс@classmethod= метод класса@staticmethod= просто функция