Объяснение @classmethod и @staticmethod в Python
Перевод: 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 или обычной функции.
То что выделено жирным не понял, можно это как-то развернуть?
Функцию classmethod также можно вызывать без создания экземпляра класса, но его определение следует за подклассом, а не за родительским классом, через наследование.
Нет выделение жирным это просто выделение, это не гиперссылка.
«Развернуть» — это была просьба объяснить подробнее
Я имел ввиду пояснить что значит «но его определение следует за подклассом, а не за родительским классом, через наследование«
В оригинальной статье это предложение выглядит так: «@classmethod functions are also callable without instantiating the class, but its definition follows Subclass, not Parent class, via inheritance.» Наверно тут подразумевается что при использование методов класса через @classmethod, вы можете эти методы переопределять в дочерних классах. И вам не нужно использовать механизм наследования. А вообще конечно запутанное предложение.
Мусор а не статья, зашел сюда что бы освежить знания, более сложных и не явных формулировок еще поискать надо. Любой новичок что зайдет на эту статью не поймет ничего.
Новичку это и не потребуется. Когда ты поймешь, что тебе это надо, у тебя точно будет достаточно знаний, чтобы разобраться.
Когда знаний достаточно и разбираться не надо — все и так понятно. Статья местамиj полезная, но хотелось бы попроще. Заключение вообще ни о чем — если нет дочерних классов .@classmethod и @staticmethodни как нельзя использовать?
Можно)
Скажи это интервьюеру на собесе
Отличная статья и все понятно! Хотя может у меня еще недостаточно знаний, для того чтобы сравнивать (оценивать), но эта статья мне была понятна
Ерунду вы пишите. Всё очень понятно. Как бы рассчитано, что уже есть базовые понятия об классах, экземплярах и в целом принципах ООП
Цитата из статьи «Для новичков, изучающих объектно-ориентированное программирование на Python, очень важно»
Странно, но я все понял 🙂
Надо было сравнение предоставить. В каких моментах один тип будет работать, а другой нет. И где один по одному сработает, а другой нет. И разжевать желательно, раз для новичков 🙂
При правильно указанных аргументах: не указывать self в @staticmethod и использовать cls в @classmethod все будет работать. Разницы не заметите.
Разница как раз кроется в деталях надо понимать что cls это ссылка на класс. Соответственно в дочерних классах можно влиять непосредственно на родительский класс.