Программирование

Разница между @classmethod и @staticmethod в Python

Что такое @classmethod и @staticmethod в Python? Чем они отличаются, когда использовать декоратор classmethod или staticmethod. Примеры кода, наследование и зачем нужны эти декораторы в классах Python.

Что означают @classmethod и @staticmethod в Python, и чем они отличаются? Когда, зачем и как их следует использовать? Как я понимаю, @classmethod указывает классу, что это метод, который должен наследоваться подклассами, но в чем смысл этого? Почему бы не определить метод класса без добавления @classmethod или @staticmethod или любых других @-декораторов?

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


Содержание


Что такое @classmethod в Python

Представьте: вы пишете класс, и вдруг нужно создать экземпляр не стандартным способом — например, из строки или из файла. Вот тут @classmethod и спасает. Этот декоратор говорит Python: “Эй, этот метод принадлежит классу целиком, передай ему класс как первый аргумент под именем cls”.

В коде это выглядит так:

python
class Person:
 species = "Homo Sapiens"
 
 def __init__(self, name):
 self.name = name
 
 @classmethod
 def from_string(cls, string):
 name = string.split()[0]
 return cls(name) # cls — это сам класс!

Здесь from_string — альтернативный конструктор. Вызовите Person.from_string("Иван Иванов"), и вуаля — новый объект. Почему cls круто? Потому что в подклассах оно автоматически подставит правильный класс. Создайте class Employee(Person): pass, и Employee.from_string("Петр Петров") вернет Employee, а не Person. Магия наследования!

Без cls метод бы не знал, какой класс использовать. А Python Engineer подчеркивает: classmethod имеет доступ к переменным класса через cls, но не к экземпляру.

Интересно, правда? А вы пробовали такие фабричные методы?


Что такое @staticmethod в Python

А теперь @staticmethod — это совсем другая история. Он как гость на вечеринке класса: живет внутри, но не лезет в карман к хозяину. Ни self, ни cls — просто функция, которая логически связана с классом.

Пример из жизни — проверка палиндрома для строк в классе TextProcessor:

python
class TextProcessor:
 @staticmethod
 def is_palindrome(text):
 return text == text[::-1]

Вызовите TextProcessor.is_palindrome("радар") — True. Или на экземпляре: processor = TextProcessor(); processor.is_palindrome("level"). Работает везде, без лишних аргументов.

DEV Community объясняет: staticmethod не трогает состояние ни класса, ни объекта. Идеально для математических утилит, валидаторов или чего-то, что не зависит от данных класса. Зачем в классе? Для организации кода — все вместе, namespace как в модуле.

Но подождите, а в чем подвох с наследованием? Оно не меняется автоматически, в отличие от classmethod.


Основные различия между @classmethod и @staticmethod

Давайте разберем по полочкам, чтобы не путаться. Главное — аргументы при вызове.

Аспект @classmethod @staticmethod
Первый аргумент cls (класс) Нет ничего
Доступ к классу Да, через cls Нет
Доступ к экземпляру Нет напрямую Нет
Наследование Автоматически cls = подкласс Как обычная функция
Вызов На классе или экземпляре То же

Из Stack Overflow ясно: classmethod для операций на уровне класса, staticmethod — чистые утилиты. А programmera.ru добавляет: без декораторов метод требует self и не вызовется на классе.

Простой тест:

python
class Test:
 @classmethod
 def class_m(cls):
 print(f"Class: {cls.__name__}")
 
 @staticmethod
 def static_m():
 print("Static method")

Test.class_m() # Class: Test
Test.static_m() # Static method

Разница видна сразу. Classmethod “знает” свой класс, staticmethod — нет.

А что если наследник? Подкласс с classmethod подхватит cls правильно, с static — вызовет версию родителя.


Когда и зачем использовать @classmethod

Используйте @classmethod, когда метод работает с классом целиком: альтернативные конструкторы, изменение классовых переменных или хуки для подклассов.

Зачем? Наследование! Ваш пример в вопросе верен: @classmethod обеспечивает, чтобы подклассы наследовали поведение правильно. Без него метод ожидает self — вызов на классе упадет с TypeError.

Реальный кейс из webdevblog.ru: десериализация JSON в объект.

python
import json

class Config:
 defaults = {"debug": False}
 
 @classmethod
 def from_json(cls, json_str):
 data = json.loads(json_str)
 config = cls(**data)
 config.defaults.update(data.get("defaults", {}))
 return config

Подкласс DevConfig(Config) автоматически создаст свой экземпляр. Круто для фреймворков вроде Django или когда классы ветвятся.

Еще: счетчики экземпляров через cls-инстансы. Но не злоупотребляйте — если не нужно cls, берите staticmethod.

Когда не использовать? Если метод не касается класса — выносите в модуль.


Когда и зачем использовать @staticmethod

@staticmethod — для “чужих” функций в классе. Валидация, конвертеры, математика. Не зависит от self или cls? Идеально.

Пример из Skypro: проверка возраста.

python
class User:
 def __init__(self, age):
 if not User.is_adult(age):
 raise ValueError("Слишком молод!")
 self.age = age
 
 @staticmethod
 def is_adult(age):
 return age >= 18

Логично группировать с User, но метод универсален. Вызов без создания объекта — бонус.

Зачем в классе? Организация. В большом проекте все утилиты по теме — вместе. Плюс, тесты проще.

Из ru.stackoverflow: staticmethod не ломается в подклассах, если логика общая.

Не используйте, если нужен доступ к классу — тогда classmethod.

А вы знали, что в Java это просто static методы? Python добавляет гибкость с вызовом на экземпляре.


Почему нельзя обойтись без декораторов

Вот ключевой момент вашего вопроса. Без @ Python думает: “Это instance method, жди self!”

python
class Bad:
 def method(cls): # Хотим cls?
 print(cls)

Bad.method() # TypeError: method() missing 1 required positional argument: 'self'

Почему self? Потому что при вызове на классе Python передает класс как self! Хаос.

Декораторы фиксят:

  • @classmethod меняет сигнатуру: descriptor возвращает bound method с cls.

  • @staticmethod: descriptor игнорирует self/cls.

Из programmera.ru: без них нет наследования cls в подклассах. Метод родителя вызовется с cls=родитель.

Альтернатива? Обычные функции вне класса. Но теряете namespace. Декораторы — элегантно.

В итоге: декораторы — не прихоть, а способ сказать Python “это не про экземпляры”.

Подклассы? Без classmethod Super.method() в Sub вызовет Super.method с self=Super, а не Sub.

Теперь ясно, почему наследование работает именно так?


Источники

  1. Stack Overflow: What is the difference between @staticmethod and @classmethod?
  2. Python Engineer: Difference between @classmethod, @staticmethod
  3. DEV Community: Class Method Vs Static Method
  4. programmera.ru: Разница между @staticmethod и @classmethod
  5. webdevblog.ru: Объяснение @classmethod и @staticmethod
  6. Skypro: Разница между @staticmethod и @classmethod
  7. ru.stackoverflow: Отличие @staticmethod и @classmethod

Заключение

@classmethod и @staticmethod решают проблему вызова методов на классе без self: первый для работы с cls и наследованием (фабрики, хуки), второй — для независимых утилит. Разница в classmethod staticmethod критична для подклассов — cls автоматически подставляется. Без декораторов все ломается на TypeError, так что не игнорируйте их: организуют код, упрощают жизнь. Попробуйте в своем проекте — увидите, как код задышит свободнее.

Авторы
Проверено модерацией
Модерация
Разница между @classmethod и @staticmethod в Python