Урок курса

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(...)?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку