Перейти к содержанию

Особые типы методов класса staticmethod и classmethod

Особые декораторы классов

Читая чужой код, вы иногда будете сталкиваться с новыми и необычными вещами. С одной из таких мы разберемся в этой лекции. Возможно, вам уже попадался такой код:

class SomeClass:

    def do(self):  # 🟢 обычный метод, доступный экземплярам класса
        # SomeClass == self.__class__ == True
        return 'Вызван обычный метод', self

    @classmethod  # 🟢 метод класса
    def class_do(cls):
        return 'Вызван метод класса class method', cls

    @staticmethod # 🟢 статический метод 
    def static_do():
        return 'Вызван статический метод static method'

Зачем программисты используют такой синтаксис и чем обычные методы, доступные экземплярам класса, отличаются от статических и методов класса? И почему сделаны такие особенные декораторы?

Методы экземпляра класса

Первый метод класса выглядит и ведет себя вполне обычно. Он получает на вход объект self и ведет себя вполне обычно. Он доступен в экземпляре класса (объекте, который получится из этого класса). Он получает только один параметр и это ссылка на экземпляр класса.

Поскольку мы уже знакомы с магическими методами, то не лишним будет добавить, что, обратившись к свойству self.__class__ вы можете получить доступ к объекту SomeClass. Например:

some = SomeClass()
some.__class__ == SomeClass  # True

Экземпляр класса может через свойство self.__class__ получать доступ к объекту класса.

Методы класса

Декоратор @classmethod уже имеет другую сигнатуру, вместо self, он получает cls. Который, является самим классом. То есть в данном случае SomeClass. Этот декоратор делает доступным функцию доступной и в экземпляре класса и в самом классе. То есть все варианты будут работать SomeClass.class_do(), SomeClass().class_do() и some.class_do().

Метод класса явно получает доступ к классу в первом параметре.

Это удобный способ организовать код таким образом, чтобы у вас была функция, которая находится в пространстве имен класса, но явно не работает с экземплярами класса. Например, так можно сделать фабрику класса, функцию, которая позволяет проще создавать объекты с пред заданными свойствами:

class Contact:
    def __init__(self, name, phone, favorite=None):
        self.name = name
        self.phone = phone
        self.favorite = favorite

    @classmethod
    def new_fav(cls, name, phone):
        return cls(name, phone, favorite=True)

obi = Contact.new_fav('Obi-Wan', '+5 (555) 555-55-55')

Статические методы

Данные методы не имеют доступа ни к классу, ни к объектам, которые из него получаются. В данном случае имя класса выступает только в качестве пространства имен. По сути, это то же самое, как если бы функция была определена рядом с классом и не имела к нему отношения. И служит только для удобства организации кода.

Декоратор @staticmethod позволяет избежать необходимости указывать self в качестве первого параметра. И метод и выглядит, и ведет себя как независимая функция.