Урок курса
classmethod, staticmethod и обычный метод в Python
Python для продвинутых: ООП, типизация и тестированиеОбычный метод получает экземпляр первым аргументом — по соглашению self. Он читает и меняет состояние конкретного объекта и может обращаться к классу через type(self).
class Account:
fee_rate = 0.02
def __init__(self, balance: float):
self.balance = balance
def fee(self) -> float:
return self.balance * self.fee_rate
Вызов account.fee() автоматически связывает account с параметром self. Если поведение зависит от данных экземпляра, это обычный метод — дополнительные декораторы не нужны.
classmethod и альтернативный конструктор
Метод с @classmethod получает класс в параметре cls. Это полезно для именованного конструктора и поведения, которое должно корректно работать у наследников.
class Account:
def __init__(self, balance: float):
self.balance = balance
@classmethod
def from_cents(cls, cents: int):
return cls(cents / 100)
Использование cls(...), а не жёсткого Account(...), сохраняет полиморфизм. Classmethod также может читать class-level конфигурацию, но общее изменяемое состояние класса требует осторожности.
staticmethod — функция в namespace класса
@staticmethod не получает автоматически ни self, ни cls. Это обычная функция, размещённая в классе, потому что предметно связана с ним.
Если функция нужна только внутренней реализации и не является частью понятного API типа, модульная функция часто проще. Staticmethod не даёт доступ к состоянию и не делает код «объектно-ориентированнее».
Выбор можно свести к трём вопросам: нужны поля конкретного объекта — instance method; нужен сам тип или альтернативный конструктор — classmethod; не нужен ни объект, ни тип, но операция естественно принадлежит публичному namespace класса — staticmethod. Не выбирайте декоратор по тому, сколько аргументов хочется скрыть.
Альтернативный конструктор, который сохраняет подкласс
Метод from_text должен создавать объект того класса, через который его вызвали. Поэтому используется classmethod и cls(...). Если внутри жёстко написать User(...), вызов через подкласс потеряет его тип. Staticmethod удобен для вспомогательного вычисления, которому не нужны ни объект, ни класс.
class User:
def __init__(self, name):
self.name = name
@classmethod
def from_text(cls, text):
return cls(text.strip())
@staticmethod
def valid_name(text):
return bool(text.strip())
class Admin(User):
pass
admin = Admin.from_text(" Anna ")
assert isinstance(admin, Admin) and admin.name == "Anna"
assert User.valid_name(" ") is False
Поменяйте в конструкторе cls на User и повторите проверку типа. Для обычного метода self обозначает конкретный экземпляр: он нужен, когда результат зависит от сохранённых полей. Добавление staticmethod к любой функции внутри класса не делает её автоматически частью удачного API.
Продолжите разбор: Класс, init, атрибуты экземпляра и методы.
Задача 1
Почему альтернативный конструктор обычно делают classmethod и возвращают cls(...)?
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
