Короткий ответ
Обычный метод получает первым аргументом экземпляр (self) и обращается к его состоянию; classmethod получает первым аргументом сам класс (cls) и обычно используется для альтернативных конструкторов; staticmethod не получает ни self, ни cls и ведёт себя как обычная функция, просто логически связанная с классом по смыслу.
Как это работает подробнее
Декоратор @classmethod превращает первый параметр в ссылку на класс, через который метод был вызван (это важно при наследовании — cls будет указывать на класс-наследник, если метод вызван через него). @staticmethod убирает автоматическую передачу чего-либо — метод просто помещён в namespace класса для группировки, без доступа к self/cls.
Пример кода
example.py
class User:
def __init__(self, name):
self.name = name
@classmethod
def from_dict(cls, data):
return cls(data["name"]) # cls — правильный класс даже для наследников
@staticmethod
def is_valid_name(name):
return bool(name) and name.isalpha()
class AdminUser(User):
pass
admin = AdminUser.from_dict({"name": "Boss"})
print(type(admin)) # <class '__main__.AdminUser'>
print(User.is_valid_name("Alice")) # TrueЧто использовать на практике
- Практика. classmethod часто используют для фабричных методов, создающих объект по-другому (
User.from_dict(data)), которые должны уметь работать и для подклассов. staticmethod применяют для вспомогательных функций, логически относящихся к классу (например, валидация формата email), но не нуждающихся ни в состоянии объекта, ни в самом классе.
Подводные камни
- Риски. Делают staticmethod там, где фактически нужен classmethod (и наоборот), из-за чего, например, фабричный метод в подклассе продолжает создавать объект родительского класса вместо подкласса. Также пытаются обратиться к
selfвнутри staticmethod и получают ошибку, так как он туда не передаётся.