Объяснение @classmethod и @staticmethod в Python

Spread the love

Перевод: Python’s @classmethod and @staticmethod Explained

Для новичков, изучающих объектно-ориентированное программирование на Python, очень важно хорошо разбираться в таких понятиях как classmethod и staticmethod для написания более оптимизированного и повторно используемого кода.

Кроме того, даже опытные программисты, работающие на разных языках, часто путают эти два понятия.

В этой статье мы разберем что это такое и какая между ними разница.

staticmethod в Python

@staticmethod — используется для создания метода, который ничего не знает о классе или экземпляре, через который он был вызван. Он просто получает переданные аргументы, без неявного первого аргумента, и его определение неизменяемо через наследование.

Проще говоря, @staticmethod — это вроде обычной функции, определенной внутри класса, которая не имеет доступа к экземпляру, поэтому ее можно вызывать без создания экземпляра класса.

Синтаксис:

class ClassName:
    @staticmethod
    def method_name(arg1, arg2, ...): ...

Здесь мы используем декоратор @staticmethod для определения статического метода в Python. Вы можете заметить, что статический метод не принимает self в качестве первого аргумента для метода.

А теперь посмотрим на пример использования.

class Myclass():
    @staticmethod
    def staticmethod():
        print('static method called')

Как мы уже говорили, мы можем получить доступ к статическому методу класса без создания экземпляра.

Myclass.staticmethod()

Результат:

static method called

Хотя вызов метода из экземпляра класса тоже возможен.

my_obj = Myclass()
my_obj.staticmethod()

Результат:

static method called

Отлично, но когда полезны статические методы?

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

class Person():
    @staticmethod
    def is_adult(age):
        if age > 18:
            return True
        else:
            return False

В приведенном выше примере мы можем проверить, является ли человек взрослым, без инициирование создания экземпляра.

Person.is_adult(23)

Результат:

True

Classmethod в Python

@classmethod — это метод, который получает класс в качестве неявного первого аргумента, точно так же, как обычный метод экземпляра получает экземпляр. Это означает, что вы можете использовать класс и его свойства внутри этого метода, а не конкретного экземпляра.

Проще говоря, @classmethod — это обычный метод класса, имеющий доступ ко всем атрибутам класса, через который он был вызван. Следовательно, classmethod — это метод, который привязан к классу, а не к экземпляру класса.

Синтаксис:

class Class:
    @classmethod
    def method(cls, arg1, arg2, ...): ... 

В данному случае декоратор @classmethod используется для создания методов класса, и cls должен быть первым аргументом каждого метода класса.

class MyClass:
    @classmethod
    def classmethod(cls):
        print('Class method called')

Функцию classmethod также можно вызывать без создания экземпляра класса, но его определение следует за подклассом, а не за родительским классом, через наследование.

MyClass.classmethod()

Результат:

Class method called

Когда использовать classmethod?

@classmethod используется, когда вам нужно получить методы, не относящиеся к какому-либо конкретному экземпляру, но тем не менее, каким-то образом привязанные к классу. Самое интересное в них то, что их можно переопределить дочерними классами.

Поэтому, если вы хотите получить доступ к свойству класса в целом, а не к свойству конкретного экземпляра этого класса, используйте classmethod.

class MyClass():
    
    TOTAL_OBJECTS=0
    
    def __init__(self):
        MyClass.TOTAL_OBJECTS = MyClass.TOTAL_OBJECTS+1
       
    @classmethod
    def total_objects(cls):
        print("Total objects: ",cls.TOTAL_OBJECTS)

# Создаем объекты        
my_obj1 = MyClass()
my_obj2 = MyClass()
my_obj3 = MyClass()

# Вызываем classmethod 
MyClass.total_objects()

Результат:

Total objects:  3

Теперь, если мы унаследуем этот класс в дочерний класс и объявим там переменную TOTAL_OBJECTS и вызовем метод класса из дочернего класса, он вернет общее количество объектов для дочернего класса.

class MyClass():
    
    TOTAL_OBJECTS=0
    
    def __init__(self):
        MyClass.TOTAL_OBJECTS = MyClass.TOTAL_OBJECTS+1
       
    @classmethod
    def total_objects(cls):
        print("Total objects: ", cls.TOTAL_OBJECTS)

# Создаем объекты родительского класса       
my_obj1 = MyClass()
my_obj2 = MyClass()


# Создаем дочерний класс
class ChildClass(MyClass):
    TOTAL_OBJECTS=0    
    pass

ChildClass.total_objects()

Результат:

Total objects:  0

Заключение

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

Также имейте в виду, что вызов @classmethod включает в себя дополнительное выделение памяти, чего нет при вызове @staticmethod или обычной функции.

Была ли вам полезна эта статья?
[265 / 4.7]

Spread the love
Подписаться
Уведомление о
guest
16 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Дмитрий
Дмитрий
4 лет назад

То что выделено жирным не понял, можно это как-то развернуть?
Функцию classmethod также можно вызывать без создания экземпляра класса, но его определение следует за подклассом, а не за родительским классом, через наследование.

Editorial Team
Администратор
4 лет назад
Reply to  Дмитрий

Нет выделение жирным это просто выделение, это не гиперссылка.

Анонимно
Анонимно
4 лет назад
Reply to  Editorial Team

«Развернуть» — это была просьба объяснить подробнее

Дмитрий
Дмитрий
4 лет назад
Reply to  Editorial Team

Я имел ввиду пояснить что значит «но его определение следует за подклассом, а не за родительским классом, через наследование«

Editorial Team
Администратор
4 лет назад
Reply to  Дмитрий

В оригинальной статье это предложение выглядит так: «@classmethod functions are also callable without instantiating the class, but its definition follows Subclass, not Parent class, via inheritance.» Наверно тут подразумевается что при использование методов класса через @classmethod, вы можете эти методы переопределять в дочерних классах. И вам не нужно использовать механизм наследования. А вообще конечно запутанное предложение.

Анонимно
Анонимно
3 лет назад

Мусор а не статья, зашел сюда что бы освежить знания, более сложных и не явных формулировок еще поискать надо. Любой новичок что зайдет на эту статью не поймет ничего.

Анонимно
Анонимно
3 лет назад

Новичку это и не потребуется. Когда ты поймешь, что тебе это надо, у тебя точно будет достаточно знаний, чтобы разобраться.

Анонимно
Анонимно
3 лет назад

Когда знаний достаточно и разбираться не надо — все и так понятно. Статья местамиj полезная, но хотелось бы попроще. Заключение вообще ни о чем — если нет дочерних классов .@classmethod и @staticmethodни как нельзя использовать?

Анонимно
Анонимно
1 год назад

Можно)

xxx
xxx
2 лет назад

Скажи это интервьюеру на собесе

Анонимно
Анонимно
3 лет назад

Отличная статья и все понятно! Хотя может у меня еще недостаточно знаний, для того чтобы сравнивать (оценивать), но эта статья мне была понятна

Last edited 3 лет назад by Анонимно
Roman
Roman
2 лет назад

Ерунду вы пишите. Всё очень понятно. Как бы рассчитано, что уже есть базовые понятия об классах, экземплярах и в целом принципах ООП

xxx
xxx
2 лет назад
Reply to  Roman

Цитата из статьи «Для новичков, изучающих объектно-ориентированное программирование на Python, очень важно»

Анонимно
Анонимно
2 лет назад

Странно, но я все понял 🙂

Анонимно
Анонимно
9 месяцев назад

Надо было сравнение предоставить. В каких моментах один тип будет работать, а другой нет. И где один по одному сработает, а другой нет. И разжевать желательно, раз для новичков 🙂

Анонимно
Анонимно
4 месяцев назад

При правильно указанных аргументах: не указывать self в @staticmethod и использовать cls в @classmethod все будет работать. Разницы не заметите.
Разница как раз кроется в деталях надо понимать что cls это ссылка на класс. Соответственно в дочерних классах можно влиять непосредственно на родительский класс.