Дескриптор classmethod при обращении через экземпляр: какой объект получает его функция первым аргументом?
Функция, объявленная как classmethod, получает первым аргументом класс, а не экземпляр. При обращении через экземпляр Python всё равно передаёт класс этого экземпляра.
Обычный метод связывается с конкретным экземпляром, потому что ему часто требуется доступ к состоянию объекта. Однако некоторым операциям нужен сам класс: например, для создания экземпляра альтернативным способом или работы с общими настройками класса.
classmethod предоставляет такой вариант связывания без ручной передачи класса вызывающим кодом.
Если метод должен работать с классом, использование обычного метода приведёт к передаче экземпляра первым аргументом. Это вынуждает писать лишний код или получать класс через type(self), что смешивает разные модели вызова.
Неверное понимание механизма может привести к ошибке типов: метод ожидает класс, но получает объект экземпляра, либо разработчик ошибочно рассчитывает на доступ к атрибутам конкретного экземпляра.
classmethod — это дескриптор. При доступе к атрибуту класса он формирует связанный метод и передаёт исходной функции класс, через который выполняется обращение.
При обращении через экземпляр владельцем связывания становится его класс. Поэтому первым аргументом будет type(экземпляра), а не сам экземпляр. При обращении через подкласс первым аргументом обычно становится именно подкласс, что позволяет корректно поддерживать наследование.
В обоих вызовах guest получает User в параметре cls. Метод не получает автоматически доступ к состоянию user; для этого предназначен обычный метод экземпляра.
У staticmethod автоматического первого аргумента нет. Обычный метод получает экземпляр, classmethod — класс, а статический метод не получает ни один из них автоматически. Компромисс заключается в выборе зависимости: classmethod удобен для фабрик и полиморфного создания объектов, но не заменяет методы, которым нужно состояние конкретного экземпляра.
В библиотеке нужно создать объект конфигурации из словаря. Вариант с обычной функцией-фабрикой прост и явно принимает класс, но при наследовании фабрику приходится переопределять или передавать подкласс вручную. Обычный метод не подходит, потому что требует уже созданный объект.
classmethod позволяет объявить альтернативный конструктор внутри класса. При вызове через подкласс он получает этот подкласс и создаёт его экземпляр, поэтому выбранное решение сохраняет расширяемость без дублирования фабричной логики.
Что произойдёт при вызове classmethod через подкласс?
Первым аргументом обычно будет подкласс, через который выполнено обращение, а не класс, в котором метод впервые объявлен. Поэтому выражение создания через cls(...) поддерживает полиморфное возвращение экземпляров подкласса.
Может ли classmethod обращаться к атрибутам экземпляра?
Нет, экземпляр ему автоматически не передаётся. Метод может обращаться к атрибутам класса через полученный cls, но для доступа к данным конкретного объекта экземпляр нужно передать явно или использовать обычный метод.
Чем classmethod отличается от ручного вызова функции класса?
Декоратор автоматически создаёт правильное связывание при обращении через класс или экземпляр. При ручном вызове функции из __dict__ можно получить сам дескриптор или функцию без ожидаемого связывания, поэтому исчезает стандартная семантика доступа к методу и возрастает риск передать неверный первый аргумент.