Python: уроки, справочник и задачи

@classmethod в Python: методы класса на примерах

Раздел: ООП в Python · Обновлено 31.10.2026

Python classmethod превращает функцию внутри класса в метод класса: первым аргументом она получает сам класс cls, а не экземпляр self. Такой метод можно вызвать без создания объекта, и он корректно работает при наследовании. Разберем синтаксис декоратора @classmethod, фабричные методы, работу с атрибутами класса и отличия от @staticmethod.

Python classmethod
Фото: Warner, Burns, Toan, and Lunde architecture firm / Public domain, Wikimedia Commons
Содержание статьи
  1. Синтаксис и принцип работы
    1. Функция classmethod() без декоратора
  2. Альтернативные конструкторы
  3. Метод класса и наследование
  4. Изменение атрибутов класса
  5. Сравнение с staticmethod и обычным методом
  6. Ограничения и подводные камни
  7. Частые вопросы
    1. Обязательно ли называть параметр cls?
    2. Можно ли вызвать метод класса через объект?
    3. Может ли метод класса вызвать обычный метод?
    4. Когда лучше staticmethod, а когда classmethod?

Синтаксис и принцип работы

Метод класса объявляют декоратором @classmethod перед def. Первый параметр по соглашению называют cls: Python сам подставит в него класс, через который произошел вызов. Имя можно выбрать любое, но другие программисты ожидают именно cls.

class Counter:
    created = 0

    def __init__(self):
        Counter.created += 1

    @classmethod
    def total(cls):
        return f"{cls.__name__}: создано {cls.created}"

Counter(); Counter()
print(Counter.total())    # Counter: создано 2
print(Counter().total())  # Counter: создано 3

Вызов работает и через класс, и через экземпляр. Во втором случае Python все равно передаст в cls класс объекта, а не сам объект. Поэтому внутри метода класса нет доступа к атрибутам конкретного экземпляра вроде self.name.

Функция classmethod() без декоратора

classmethod - встроенная функция, и декоратор лишь короткая запись ее вызова. Код ниже делает то же самое, что @classmethod, но читается хуже, поэтому в реальных проектах его почти не пишут.

class Person:
    age = 25

    def print_age(cls):
        print("Возраст:", cls.age)

    print_age = classmethod(print_age)

Person.print_age()  # Возраст: 25

Подробнее о том, как устроены декораторы и почему запись через @ равна повторному присваиванию, рассказано в статье декораторы в Python.

Альтернативные конструкторы

Главное применение методов класса - фабричные методы, которые создают объект из данных другого формата. У класса один __init__, а способов получить исходные данные бывает несколько: строка из файла, словарь из JSON, год рождения вместо возраста. Метод класса разбирает входные данные и вызывает cls(...).

from datetime import date

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def from_birth_year(cls, name, year):
        return cls(name, date.today().year - year)

    @classmethod
    def from_string(cls, line):
        name, age = line.split(";")
        return cls(name.strip(), int(age))

p1 = Person.from_birth_year("Иван", 2000)
p2 = Person.from_string("Анна; 31")
print(p1.name, p1.age, p2.name, p2.age)

Такой прием встречается и в стандартной библиотеке: dict.fromkeys(), datetime.fromisoformat(), datetime.fromtimestamp(), int.from_bytes() - все это методы класса.

Метод класса и наследование

Преимущество cls перед явным именем класса проявляется в наследниках. Если фабрика написана через cls(...), то при вызове из дочернего класса она вернет объект дочернего класса, и переопределять ее не придется.

class Shape:
    def __init__(self, size):
        self.size = size

    @classmethod
    def unit(cls):
        return cls(1)

class Square(Shape):
    def area(self):
        return self.size ** 2

sq = Square.unit()
print(type(sq).__name__, sq.area())  # Square 1

Если бы внутри unit() стояло return Shape(1), вызов Square.unit() вернул бы объект без метода area(). Механизм поиска методов в цепочке классов описан в материале про наследование классов.

Изменение атрибутов класса

Методы класса удобны для работы с общим состоянием: счетчиками, настройками, реестрами объектов. Присваивание через cls.атрибут меняет значение для всех экземпляров, а не создает копию в одном объекте.

class Config:
    debug = False

    @classmethod
    def enable_debug(cls):
        cls.debug = True

a, b = Config(), Config()
Config.enable_debug()
print(a.debug, b.debug)  # True True

Учтите, что при вызове из наследника cls указывает на наследника, и присваивание создаст атрибут уже у него, не затронув родителя. Иногда это нужно, иногда приводит к неожиданному поведению, поэтому для общего на всю иерархию значения пишут имя базового класса явно.

Сравнение с staticmethod и обычным методом

В классе Python бывают три вида методов. Они отличаются тем, что получают первым аргументом.

Вид методаПервый аргументДоступ к даннымТипичная задача
обычныйself - экземпляратрибуты объекта и классаповедение конкретного объекта
@classmethodcls - класстолько атрибуты классафабрики, общее состояние
@staticmethodничего не получаеттолько переданные аргументывспомогательная функция по теме класса
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    @staticmethod
    def c_to_f(c):
        return c * 9 / 5 + 32

    @classmethod
    def from_fahrenheit(cls, f):
        return cls((f - 32) * 5 / 9)

print(Temperature.c_to_f(100))                # 212.0
print(Temperature.from_fahrenheit(212).celsius)  # 100.0

Правило выбора простое: если методу нужен класс, берите @classmethod, если не нужен ни класс, ни объект, подойдет @staticmethod. Общие принципы устройства классов собраны в статье классы и объекты.

Ограничения и подводные камни

Справка по встроенной функции есть в документации Python.

Частые вопросы

Ответы на вопросы, которые возникают при первом знакомстве с методами класса.

Обязательно ли называть параметр cls?

Нет, Python передаст класс в первый параметр под любым именем. Но cls - общепринятое соглашение, и линтеры предупреждают об отступлении от него.

Можно ли вызвать метод класса через объект?

Да. Вызов obj.method() работает, и в cls попадет type(obj). Результат будет таким же, как у type(obj).method().

Может ли метод класса вызвать обычный метод?

Напрямую нет, потому что у него нет экземпляра. Сначала нужно создать объект через cls(...), а затем вызвать у него нужный метод.

Когда лучше staticmethod, а когда classmethod?

Если функция создает объект или читает атрибуты класса, выбирайте classmethod. Если это просто утилита, логически связанная с классом, например проверка формата строки, хватит staticmethod.