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

Применение статических методов и методов класса

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

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

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

Кроме того, в языке Python поддерживается также понятие методов класса. На практике методы класса используются реже, и в первом аргументе им автоматически передается объект класса, независимо от того, вызываются они через имя класса или через экземпляр. Такие методы могут получить доступ к данным класса через аргумент self, даже когда они вызываются относительно экземпляра. Обычные методы (которые формально называются методами экземпляра) при вызове получают подразумеваемый экземпляр, а статические методы и методы класса – нет.

Пример реализации статических методов и методов класса

На сегодняшний день существует еще одна возможность писать простые функции, связанные с классом, которые могут вызываться через имя класса или через экземпляры. Начиная с версии Python 2.2, имеется возможность создавать классы со статическими методами и с методами класса, ни один из которых не требует передачи экземпляра класса в виде аргумента. Чтобы определить такие методы, в классах необходимо вызывать встроенные функции staticmethod и classmethod, как упоминалось в обсуждении классов нового стиля. Обе функции помечают объект функции как специальный, то есть как не требующий передачи экземпляра, в случае применения функции staticmethod, и как требующий передачи класса, в случае применения функции classmethod.

Например:

class Methods:
    def imeth(self, x): # Обычный метод экземпляра
        print(self, x)
    def smeth(x): # Статический метод: экземпляр не передается
        print(x)
    def cmeth(cls, x): # Метод класса: получает класс, но не экземпляр
        print(cls, x)
    smeth = staticmethod(smeth) # Сделать smeth статическим методом
    cmeth = classmethod(cmeth) # Сделать cmeth методом класса.

Обратите внимание, как две последние операции присваивания в этом фрагменте просто переприсваивают имена методов smeth и cmeth. Атрибуты создаются и изменяются с помощью операции присваивания в инструкции class, поэтому эти заключительные операции присваивания переопределяют инструкции def,  выполненные ранее.

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

Пример вызова статического метода:

>>> Methods.smeth(3) # Вызов статического метода, через имя класса
3 # Экземпляр не передается и не ожидается
>>> obj.smeth(4) # Вызов статического метода, через экземпляр
4 # Экземпляр не передается

В методах класса интерпретатор автоматически передает
методам класса сам класс (а не экземпляр) в первом аргументе, независимо от
того, вызываются они через имя класса или через экземпляр:

Пример вызова метода класса:

>>> Methods.cmeth(5) # Вызов метода класса, через имя класса
 5 # Будет преобразован в вызов cmeth(Methods, 5)
>>> obj.cmeth(6) # Вызов метода класса, через экземпляр
 6 # Будет преобразован в вызов cmeth(Methods, 6)

Пример программы подсчета количества экземпляра классов с использованием статического метода:

class Spam: # Для доступа к данным класса используется
    numInstances = 0 # статический метод
    def __init__(self):
       Spam.numInstances += 1
    def printNumInstances():
       print(“Number of instances:”, Spam.numInstances)
    printNumInstances = staticmethod(printNumInstances)


Вызов:

>>> a = Spam()
>>> b = Spam()
>>> c = Spam()
>>> Spam.printNumInstances() # Вызывается, как простая функция
Number of instances: 3
>>> a.printNumInstances() # Аргумент с экземпляром не передается
Number of instances: 3

Фактически методы класса всегда получают ближайший класс в дереве наследования, поэтому:

  • Применение статических методов, в которых явно указывается имя класса, может оказаться более удачным решением для обработки данных класса.
  • Методы классов лучше подходят для обработки данных, которые могут отличаться для каждого конкретного класса в иерархии.