Staticmethod python что это
Объяснение @classmethod и @staticmethod в Python
Для новичков, изучающих объектно-ориентированное программирование на Python, очень важно хорошо разбираться в таких понятиях как classmethod и staticmethod для написания более оптимизированного и повторно используемого кода.
Кроме того, даже опытные программисты, работающие на разных языках, часто путают эти два понятия.
В этой статье мы разберем что это такое и какая между ними разница.
staticmethod в Python
@staticmethod — используется для создания метода, который ничего не знает о классе или экземпляре, через который он был вызван. Он просто получает переданные аргументы, без неявного первого аргумента, и его определение неизменяемо через наследование.
Проще говоря, @staticmethod — это вроде обычной функции, определенной внутри класса, которая не имеет доступа к экземпляру, поэтому ее можно вызывать без создания экземпляра класса.
Синтаксис:
Здесь мы используем декоратор @staticmethod для определения статического метода в Python. Вы можете заметить, что статический метод не принимает self в качестве первого аргумента для метода.
А теперь посмотрим на пример использования.
Как мы уже говорили, мы можем получить доступ к статическому методу класса без создания экземпляра.
Результат:
Хотя вызов метода из экземпляра класса тоже возможен.
Результат:
Отлично, но когда полезны статические методы?
Статический метод помогает в достижении инкапсуляции в классе, поскольку он не знает о состоянии текущего экземпляра. Кроме того, статические методы делают код более читабельным и повторно используемым, а также более удобным для импорта по сравнению с обычными функциями, поскольку каждую функцию не нужно отдельно импортировать.
В приведенном выше примере мы можем проверить, является ли человек взрослым, без инициирование создания экземпляра.
Результат:
Classmethod в Python
@classmethod — это метод, который получает класс в качестве неявного первого аргумента, точно так же, как обычный метод экземпляра получает экземпляр. Это означает, что вы можете использовать класс и его свойства внутри этого метода, а не конкретного экземпляра.
Проще говоря, @classmethod — это обычный метод класса, имеющий доступ ко всем атрибутам класса, через который он был вызван. Следовательно, classmethod — это метод, который привязан к классу, а не к экземпляру класса.
Синтаксис:
В данному случае декоратор @classmethod используется для создания методов класса, и cls должен быть первым аргументом каждого метода класса.
Функцию classmethod также можно вызывать без создания экземпляра класса, но его определение следует за подклассом, а не за родительским классом, через наследование.
Результат:
Когда использовать classmethod?
@classmethod используется, когда вам нужно получить методы, не относящиеся к какому-либо конкретному экземпляру, но тем не менее, каким-то образом привязанные к классу. Самое интересное в них то, что их можно переопределить дочерними классами.
Поэтому, если вы хотите получить доступ к свойству класса в целом, а не к свойству конкретного экземпляра этого класса, используйте classmethod.
Результат:
Теперь, если мы унаследуем этот класс в дочерний класс и объявим там переменную TOTAL_OBJECTS и вызовем метод класса из дочернего класса, он вернет общее количество объектов для дочернего класса.
Результат:
Заключение
@classmethod используется в суперклассе для определения того, как метод должен вести себя, когда он вызывается разными дочерними классами. В то время как @staticmethod используется, когда мы хотим вернуть одно и то же, независимо от вызываемого дочернего класса.
Также имейте в виду, что вызов @classmethod включает в себя дополнительное выделение памяти, чего нет при вызове @staticmethod или обычной функции.
Python: статические методы, методы класса и экземпляра класса
Согласно модели данных Python, язык предлагает три вида методов: статические, класса и экземпляра класса. Давайте посмотрим, что же происходит за кулисами каждого из видов методов. Понимание принципов их работы поможет в создании красивого и эффективного кода. Начнём с самого простого примера, в котором демонстрируются все три вида методов.
Методы экземпляра класса
Это наиболее часто используемый вид методов. Методы экземпляра класса принимают объект класса как первый аргумент, который принято называть self и который указывает на сам экземпляр. Количество параметров метода не ограничено.
Встроенный пример метода экземпляра — str.upper() :
Методы класса
Методы класса привязаны к самому классу, а не его экземпляру. Они могут менять состояние класса, что отразится на всех объектах этого класса, но не могут менять конкретный объект.
Встроенный пример метода класса — dict.fromkeys() — возвращает новый словарь с переданными элементами в качестве ключей.
Статические методы
Их можно воспринимать как методы, которые “не знают, к какому классу относятся”.
Таким образом, статические методы прикреплены к классу лишь для удобства и не могут менять состояние ни класса, ни его экземпляра.
С теорией достаточно. Давайте разберёмся с работой методов, создав объект нашего класса и вызвав поочерёдно каждый из методов: instancemethod, classmethod and staticmethod.
Теперь давайте вызовем метод класса:
Мы видим, что метод класса classmethod() имеет доступ к самому классу ToyClass, но не к его конкретному экземпляру объекта. Запомните, в Python всё является объектом. Класс тоже объект, который мы можем передать функции в качестве аргумента.
Заметьте, что self и cls — не обязательные названия и эти параметры можно называть иначе.
Это лишь общепринятые обозначения, которым следуют все. Тем не менее они должны находиться первыми в списке параметров.
Вызовем статический метод:
Да, это может вас удивить, но статические методы можно вызывать через объект класса. Вызов через точку нужен лишь для удобства. На самом же деле в случае статического метода никакие аргументы ( self или cls ) методу не передаются.
То есть статические методы не могут получить доступ к параметрам класса или объекта. Они работают только с теми данными, которые им передаются в качестве аргументов.
Теперь давайте вызовем те же самые методы, но на самом классе.
Метод класса и статический метод работают, как нужно. Однако вызов метода экземпляра класса выдаёт TypeError, так как метод не может получить на вход экземпляр класса.
Теперь, когда вы знаете разницу между тремя видами методов, давайте рассмотрим реальный пример для понимания того, когда и какой метод стоит использовать. Пример взят отсюда.
Когда использовать каждый из методов?
Выбор того, какой из методов использовать, может показаться достаточно сложным. Тем не менее с опытом этот выбор делать гораздо проще.
Чаще всего метод класса используется тогда, когда нужен генерирующий метод, возвращающий объект класса. Как видим, метод класса from_birth_year используется для создания объекта класса Person по году рождения, а не возрасту.
Статические методы в основном используются как вспомогательные функции и работают с данными, которые им передаются.
Статические методы в Python
Ранее было сказано, с определенным допущением классы можно рассматривать как модули, содержащие переменные со значениями и функции. Только здесь переменные называются полями или свойствами, а функции – методами. Вместе поля и методы называются атрибутами.
Однако в случае классов, когда метод применяется к объекту, этот экземпляр передается в метод в качестве первого аргумента:
Вызов a.meth() на самом деле преобразуется к A.meth(a), то есть мы идем к «модулю A» и в его пространстве имен ищем атрибут meth. Там оказывается, что meth это функция, принимающая один обязательный аргумент. Тогда ничего не мешает сделать так:
В таком «модульном формате» вызова методов передавать объект-экземпляр именно класса A совсем не обязательно. Однако нельзя сделать так:
Если объект передается методу в нотации через точку, то этот метод должен быть описан в том классе, которому принадлежит объект, или в родительских классах. В данном случае у класса int нет метода meth(). Объект b классу A не принадлежит. Поэтому интерпретатор никогда не найдет метод meth().
Что делать, если возникает необходимость в методе, который не принимал бы объект данного класса в качестве аргумента? Да, мы можем объявить метод вообще без параметров и вызывать его только через класс:
Получается странная ситуация. Ведь meth() вызывается не только через класса, но и через порожденные от него объекты. Однако в последнем случае всегда будет возникать ошибка. То есть имеется потенциально ошибочный код. Кроме того, может понадобиться метод с параметрами, но которому не надо передавать экземпляр данного класса.
В ряде языков программирования, например в Java, для таких ситуаций предназначены статические методы. При описании этих методов в их заголовке ставится ключевое слово static. Такие методы могут вызываться через объекты данного класса, но сам объект в качестве аргумента в них не передается.
В Python острой необходимости в статических методах нет, так как код может находиться за пределами класса, и программа не начинает выполняться из класса. Если нам нужна просто какая-нибудь функция, мы можем определить ее в основной ветке. В Java это не так. Там, не считая импортов, весь код находится внутри классов. Поэтому методы, не принимающие объект данного класса и играющие роль обычных функций, необходимы. Статические методы решают эту проблему.
Однако в Python тоже можно реализовать подобное, то есть статические методы, с помощью декоратора @staticmethod:
Пример с параметром:
Статические методы в Python – по-сути обычные функции, помещенные в класс для удобства и находящиеся в пространстве имен этого класса. Это может быть какой-то вспомогательный код. Вообще, если в теле метода не используется self, то есть ссылка на конкретный объект, следует задуматься, чтобы сделать метод статическим. Если такой метод необходим только для обеспечения внутренних механизмов работы класса, то возможно его не только надо объявить статическим, но и скрыть от доступа из вне.
Пусть у нас будет класс «Цилиндр». При создании объектов от этого класса у них заводятся поля высота и диаметр, а также площадь поверхности. Вычисление площади можно поместить в отдельную статическую функцию. Она вроде и относится к цилиндрам, но с другой стороны само вычисление объекта не требует и может быть использовано где угодно.
В примере вызов make_area() за пределами класса возможен в том числе через экземпляр. При этом понятно, в данном случае свойство area самого объекта a не меняется. Мы просто вызываем функцию, находящуюся в пространстве имен класса.
Практическая работа
Приведенный в конце урока пример плохой. Мы можем менять значения полей dia и h объекта за пределами класса простым присваиванием (например, a.dia = 10). При этом площадь никак не будет пересчитываться. Также мы можем назначить новое значение для площади, как простым присваиванием, так и вызовом функции make_area() с последующим присваиванием. Например, a.area = a.make_area(2, 3). При этом не меняются высота и диаметр.
Защитите код от возможных логических ошибок следующим образом:
Свойствам dia и h объекта по-прежнему можно выполнять присваивание за пределами класса. Однако при этом «за кулисами» происходит пересчет площади, т. е. изменение значения area.
Свойству area нельзя присваивать за пределами класса. Можно только получать его значение.
Подсказка: вспомните про метод __setattr__(), упомянутый в уроке про инкапсуляцию.
Курс с примерами решений практических работ:
android-приложение, pdf-версия
С. Шапошникова © 2021
Объектно-ориентированное программирование на Python
Простое объяснение методов класса, экземпляра класса и статических методов в Python
Хочешь знать больше о Python?
Подпишись на наш канал о Python в Telegram!
Перевод статьи «Python’s Instance, Class, and Static Methods Demystified».
В этой статье я постараюсь объяснить, что стоит за методами класса, статическими методами и обычными методами экземпляра класса.
Если вы сможете достигнуть интуитивного понимания разницы между этими методами, вы также сможете писать объектно-ориентированный код на Python. Такой код проще для понимания, а в долгосрочной перспективе его легче поддерживать.
Обзор статических методов, методов класса и экземпляра класса
Давайте начнем с написания класса (на Python 3), который будет содержать простые примеры методов всех трех видов:
Примечание для пользователей Python 2. Декораторы @staticmethod и @classmethod доступны в Python начиная с версии 2.4, а значит, этот пример тоже будет работать. Вместо использования простого объявления класса class MyClass: вы можете объявить класс нового стиля, который будет наследоваться от object, с синтаксисом class MyClass(object):. Все остальное не требует каких-то уточнений.
Методы экземпляра класса
Первый метод в MyClass, под названием method, это обычный метод экземпляра класса. Это самый базовый вид методов, которым вы будете пользоваться чаще всего. Как видите, этот метод принимает один параметр (self), который при вызове метода указывает на экземпляр MyClass (хотя методы могут принимать и больше одного параметра).
При помощи параметра self методы экземпляра класса могут иметь свободный доступ к атрибутам и другим методам того же объекта. Благодаря этому они на многое способны, когда речь заходит о модификации состояния объекта.
Методы экземпляра также могут иметь доступ к самому классу — при помощи атрибута self.__class__. Это означает, что они могут менять состояние не только объекта, но и класса.
Методы класса
Давайте сравним то, что мы узнали о методах экземпляра класса, со вторым методом — MyClass.classmethod. Я обозначил этот метод при помощи декоратора @classmethod, чтобы было видно, что это метод класса.
Методы класса вместо параметра self принимают параметр cls. Этот параметр при вызове метода указывает не на экземпляр объекта, а на класс.
Поскольку метод класса имеет доступ только к аргументу cls, он не может изменять состояние экземпляра объекта. Для этого нужен доступ к self. Но, тем не менее, методы класса могут изменять состояние класса в целом, что затронет и все экземпляры этого класса.
Статические методы
Третий метод, MyClass.staticmethod, обозначен при помощи декоратора @staticmethod, чтобы показать, что этот метод статический.
Методы такого типа не принимают в качестве параметра ни self, ни cls (хотя, безусловно, они свободно могут принимать другие параметры в любых количествах).
Таким образом, статический метод не может изменять состояние ни объекта, ни класса. Виды данных, которые могут принимать статические методы, ограничены. Эти методы помещаются в класс, просто чтобы они находились в пространстве имен этого класса, т. е., для организационных целей.
Давайте посмотрим, как все это работает!
Пока что все изложенное было чистой теорией. Но я считаю, что вы должны научиться интуитивно улавливать разницу между методами на практике. Сейчас мы разберем несколько более конкретных примеров.
Давайте посмотрим, как все эти методы ведут себя, когда мы их вызываем. Начнем с создания экземпляра класса, а затем вызовем все три метода.
В MyClass реализация каждого метода возвращает сведения, благодаря которым мы можем понимать, к каким частям класса или объекта может иметь доступ метод, а также отслеживать происходящее.
Вот что происходит при вызове метода экземпляра класса:
Мы видим, что method (т. е., метод экземпляра класса) имеет доступ к экземпляру объекта (это видно по выводу ) при помощи аргумента self.
При вызове этого метода Python замещает аргумент self экземпляром объекта (obj). Мы можем проигнорировать синтаксический сахар dot-call синтаксиса (obj.method()) и получить тот же результат, передав экземпляр объекта вручную:
Можете догадаться, что произойдет, если вы попытаетесь вызвать этот метод без первоначального создания экземпляра класса?
Кстати, методы экземпляра класса при помощи атрибута self.__class__ также могут иметь доступ и к самому классу. Это делает данные методы особенно полезными в условиях ограничений доступа: они могут изменять состояние экземпляра объекта и самого класса.
Теперь давайте испытаем метод класса:
Стоит отметить, что при вызове MyClass.classmethod() Python автоматически передает класс в качестве первого аргумента функции. Это поведение Python запускается, если метод вызывается при помощи dot-синтаксиса. В методах экземпляра класса аналогично работает параметр self.
Пожалуйста, обратите внимание, что эти параметры именуются self и cls лишь в силу соглашений. С тем же успехом вы можете назвать их the_object и the_class. Важно то, что они идут первыми в списке параметров метода.
А теперь давайте вызовем статический метод:
Как видите, мы успешно вызвали staticmethod() через объект. Некоторые разработчики удивляются, когда узнают, что можно вызывать статический метод через экземпляр объекта.
Просто когда при вызове статического метода с использованием dot-синтаксиса не передаются аргументы self или cls, Python применяет ограничения доступа.
Этот пример подтверждает, что статические методы не имеют доступа ни к состоянию экземпляра объекта, ни к состоянию класса. Они работают как обычные функции, но при этом относятся к пространству имен класса (и каждого его экземпляра).
Теперь давайте посмотрим, что произойдет, если мы попытаемся вызвать эти методы в самом классе, т. е., без предварительного создания экземпляра объекта:
Этого и следовало ожидать: в этот раз мы не создавали экземпляр объекта и попытались вызвать функцию экземпляра класса прямо из самого класса. Это означает, что у Python не было никакой возможности заполнить аргумент self и, как следствие этого, вызов метода провалился.
Это должно более четко разграничить три вида методов. Но я на этом не остановлюсь. В следующих двух разделах я разберу два немного более реалистичных примера использования разных видов методов.
Мои примеры будут основаны на классе Pizza:
Примечание. В этом примере кода (а также в последующих) для форматирования строки, возвращаемой при помощи __repr__, мы будем использовать Python 3.6 f-strings. В Python 2 и версиях Python 3 до 3.6 для форматирования строки следует использовать другие выражения, например:
Фабрики вкусной пиццы и @classmethod
Наверняка вы знаете, что существует множество вкусных вариантов пиццы:
Итальянцы придумали свои рецепты пиццы столетия назад, так что все они уже имеют какие-то устоявшиеся названия. Мы этим воспользуемся и дадим пользователям нашего класса Pizza лучший интерфейс для создания тех объектов пиццы, о которых они мечтают.
Это можно сделать красиво и чисто, используя методы класса в качестве фабричных функций для разных видов пиццы:
Обратите внимание, что я использую аргумент cls в фабричных методах margherita и prosciutto, а не вызываю конструктор Pizza напрямую.
Вы можете использовать этот прием, чтобы придерживаться принципа DRY (Don’t Repeat Yourself, «Не повторяйся»). Если в какой-то момент мы решим переименовать этот класс, нам не придется обновлять еще и имя конструктора во всех фабричных функциях classmethod.
Что же мы можем сделать при помощи этих фабричных методов? Давайте испытаем их:
Как видите, мы можем использовать фабричные функции для создания новых объектов Pizza в нужной нам конфигурации. Внутри все они используют один конструктор __init__ и по сути просто предоставляют шорткат для запоминания всех возможных ингредиентов.
Можно взглянуть на это применение методов класса и под другим углом: они позволяют вам определить альтернативные конструкторы для ваших классов.
Python допускает только один метод __init__ для каждого класса. Использование методов класса позволяет добавить столько альтернативных конструкторов, сколько нужно. Таким образом вы можете сделать интерфейс ваших классов самодокументированным (в определенной степени) и упростить их использование.
Когда стоит использовать статические методы
Здесь придумать хороший пример немного сложнее. Но знаете что? Я просто продолжу растягивать аналогию с пиццей.
Вот что я придумал:
Что здесь изменилось? Для начала, я изменил конструктор и __repr__, чтобы принимался дополнительный аргумент radius.
Также я добавил метод экземпляра класса area(), который вычисляет и возвращает площадь пиццы (это также хороший кандидат для @property, но пример у нас игрушечный).
Вместо вычисления площади непосредственно в area(), с использованием всем известной формулы площади круга, я вынес его в отдельный статический метод circle_area().
Конечно, этот пример несколько простоват, но он хорошо помогает объяснить некоторые преимущества статических методов.
Как мы уже знаем, статические методы не имеют доступа к состоянию класса или объекта, потому что не принимают аргументы cls или self. Это большое ограничение, но также и отличный сигнал, показывающий, что данный метод ни от чего не зависит.
В приведенном выше примере ясно, что circle_area() никоим образом не может изменять класс или экземпляр класса. (Конечно, это всегда можно обойти при помощи глобальной переменной, но здесь мы не будем это разбирать).
Так в чем же польза?
Когда вы обозначаете метод в качестве статического, это не только подсказывает читателю, что данный метод не будет модифицировать класс или экземпляр класса — это ограничение также применяется средой выполнения Python.
Подобные приемы позволяют четко описывать элементы вашей архитектуры классов. Конечно, эти ограничения можно легко обойти. Но на практике они помогают избежать случайных модификаций, идущих вразрез с оригинальным дизайном.
Другими словами, использование статических методов и методов классов это способ сообщить о намерениях разработчика. Одновременно это способ обеспечить осуществление этих намерений в достаточной мере, чтобы избежать большинства ошибок, которые можно было бы допустить по невнимательности, и багов, которые разрушили бы дизайн.
Написание некоторых из ваших методов подобным образом (нечасто и там, где это имеет смысл) может принести пользу в плане поддерживаемости кода. Также это снижает вероятность того, что другие разработчики используют ваши классы неправильно.
Статические методы также имеют преимущество при тестировании кода.
Поскольку метод circle_area() совершенно не зависит от остального класса, его гораздо проще тестировать. Мы можем это сделать при помощи модульного теста, не беспокоясь об экземпляре класса в целом. То есть, этот метод тестируется, как обычная функция. Опять же, это облегчает поддержку кода в будущем.
Статические переменные и методы в Python
Статическая переменная и статический метод – это широко используемые концепции программирования на различных языках, таких как C ++, PHP, Java и т. l. Эти переменные и методы принадлежат классу и объектам. В этом разделе мы узнаем, как создать статические переменные и методы в Python.
Что такое статическая переменная Python?
Когда мы объявляем переменную внутри класса, но вне метода, она называется статической переменной или переменной класса. Ее можно вызвать непосредственно из класса, но не через экземпляры класса. Однако статические переменные сильно отличаются от других членов и не конфликтуют с тем же именем переменной в программе Python.
Давайте рассмотрим программу, демонстрирующую использование статических переменных в Python.
В приведенном выше примере dept – это переменная класса, определенная вне методов класса и внутри определения класса. Где имя и идентификатор – это переменная экземпляра, определенная внутри метода.
Доступ к статической переменной с использованием того же объекта класса
Мы можем напрямую обращаться к статической переменной в Python, используя тот же объект класса с оператором точки.
Давайте рассмотрим программу для доступа к статической переменной в Python с использованием того же объекта класса.
Статический метод
Python имеет статический метод, принадлежащий классу. Это похоже на статическую переменную, которая привязана к классу, а не к объекту класса. Статический метод можно вызвать без создания объекта для класса.
Это означает, что мы можем напрямую вызвать статический метод со ссылкой на имя класса. Более того, статический метод ограничен классом; следовательно, он не может изменить состояние объекта.
Особенности статических методов
Ниже приведены особенности статического метода:
Есть два способа определить статический метод в Python:
Использование метода staticmethod()
Staticmethod() – это встроенная функция в Python, которая используется для возврата заданной функции как статического метода.
Staticmethod() принимает единственный параметр. Где переданный параметр – это функция, которую необходимо преобразовать в статический метод.
Давайте рассмотрим программу для создания функции как статического метода с использованием staticmethod() в Python.
В приведенной выше программе мы объявили метод Math_num, метод Sci_num и метод Eng_num как статический метод вне класса с помощью функции staticmethod(). После этого мы можем вызвать статический метод напрямую, используя имя класса Marks.
Использование декоратора @staticmethod
@Staticmethod – это встроенный декоратор, который определяет статический метод внутри класса. Он не получает никаких аргументов в качестве ссылки на экземпляр класса или класс, вызывающий сам статический метод.
Примечание. @Staticmethod – это современный подход к определению метода как статического, и большая часть программистов использует этот подход в программировании на Python.
Давайте создадим программу для определения статического метода с помощью декоратора @staticmethod в Python.
Доступ к статическому методу с использованием того же объекта класса
Рассмотрим программу для доступа к статическому методу класса с помощью @staticmethod в Python.
Функция возвращает значение с помощью статического метода
Напишем программу для возврата значения с помощью метода @static в Python.