В чем преимущество использования статических методов в Python?


90

Я столкнулся с ошибкой несвязанного метода в python с кодом

import random

class Sample(object):
'''This class defines various methods related to the sample'''

    def drawSample(samplesize,List):
        sample=random.sample(List,samplesize)
        return sample

Choices=range(100)
print Sample.drawSample(5,Choices)

Прочитав здесь много полезных сообщений, я подумал, как можно добавить @staticmethodвыше, чтобы код заработал. Я новичок в питоне. Может кто-нибудь объяснить, почему нужно определять статические методы? Или почему не все методы определены как статические?


1
Это странный вопрос. Статические методы конструируют необходимость . Это не «преимущество». Вы используете их, потому что должны. Наличие статических методов - это конструктивная особенность класса. Вы спрашиваете, что такое статические методы в первую очередь? Думаю, вопрос можно было бы переформулировать, чтобы более четко определить, что вам нужно знать.
S.Lott

15
Нет, я не хотел знать, что это такое. Я хотел знать, почему это «необходимость», что стало ясно из ответов, данных другими. Именно тогда вы определили бы его, а не нестатические методы. Спасибо.
Curious2learn

3
@ S.Lott: Когда необходимо использовать статический метод вместо использования обычного метода класса? Насколько я могу судить, метод класса может делать все, что может статический метод. Статический метод действительно имеет «преимущества», перечисленные в другом месте в этом сообщении, но я не вижу причин, по которым метод класса нельзя использовать в любом месте, где можно использовать статический метод, что делает его необходимостью.
RFV

Ответы:


128

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

Поэтому они бесполезны для повседневных методов.

Однако они могут быть полезны для группировки некоторой служебной функции вместе с классом - например, для простого преобразования из одного типа в другой - которому не требуется доступ к какой-либо информации, кроме предоставленных параметров (и, возможно, некоторых атрибутов, глобальных для модуля. )

Они могут быть вынесены за пределы класса, но их группировка внутри класса может иметь смысл там, где они применимы только там.

Вы также можете ссылаться на метод через экземпляр или класс, а не через имя модуля, что может помочь читателю понять, к какому экземпляру относится метод.


8
@ Curious2learn: не все , но некоторые методы полезны как статические. Подумайте о примере Localeкласса, экземпляры которого будут локали (да). getAvailableLocales()Метод был бы хорошим пример метода статического такого класса: это явно принадлежит к классу Locale, а также явно не принадлежит к какому - либо конкретному случаю.
MestreLion

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

Редактор указывает, что статический метод может получить доступ к атрибутам класса, явно переходя вниз от class_name.attribute, просто не cls.attributeили self.attribute. Это верно для «общедоступных» атрибутов. По соглашению вам не следует обращаться к атрибутам, скрытым с подчеркиванием, или именам, искаженным двумя символами подчеркивания таким образом. Это также означает, что ваш код будет более хрупким, когда атрибуты смещаются в иерархии наследования.
Oddthinking

Цитата из Гвидо, которая помогает решить, когда использовать статические методы (никогда): «Мы все знаем, насколько ограничены статические методы. (По сути, это случайность - еще во времена Python 2.2, когда я изобретал классы и дескрипторы нового стиля. , Я хотел реализовать методы класса, но сначала я их не понял и случайно реализовал сначала статические методы. Потом было уже поздно их убирать и предоставлять только методы класса »,
Борис Чурзин,

189

См. Эту статью для подробного объяснения.

TL; DR

1. Это исключает использование selfаргументов.

2. Это уменьшает использование памяти, потому что Python не должен создавать экземпляр метода привязки для каждого инициированного объекта:

>>>RandomClass().regular_method is RandomClass().regular_method
False
>>>RandomClass().static_method is RandomClass().static_method
True
>>>RandomClass.static_method is RandomClass().static_method
True

3. Это улучшает читаемость кода, что означает, что метод не зависит от состояния самого объекта.

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


3
Это должен быть принятый ответ. Тот факт, что статический метод для любых экземпляров и сам класс являются одним и тем же объектом, является реальным преимуществом, особенно когда у вас много экземпляров (например, каждый экземпляр для изменяемой записи базы данных).
Zhuoyun Wei

+1 и согласен с @ZhuoyunWei. Единственный ответ, который объясняет несколько причин, по которым статический метод иногда предпочтительнее, чем метод класса (хотя 1 и 3 на самом деле являются одной и той же причиной).
Alex

1
Лучший ответ на «почему статический метод», но исходный вопрос также задан «почему не все методы статичны?». Краткое «нет доступа к атрибутам экземпляра» тоже охватывает это.
Exu

ИМО, единственное реальное преимущество заключается в том, что вы можете переопределить его в подклассе, но даже у этого есть некоторое ощущение, что вы делаете что-то неправильно с точки зрения ООП, в каком случае вы бы это сделали?
Борис Чурзин,

23

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

Я бы никогда не исправил приведенный выше код, сделав метод статическим. Я бы либо отказался от класса, а просто написал функцию:

def drawSample(samplesize,List):
    sample=random.sample(List,samplesize)
    return sample

Choices=range(100)
print drawSample(5,Choices)

Если у вас много связанных функций, вы можете сгруппировать их в модуль, т. Е. Поместить их все в один файл с именем, sample.pyнапример; тогда

import sample

Choices=range(100)
print sample.drawSample(5,Choices)

Или я бы добавил __init__в класс метод и создал бы экземпляр с полезными методами:

class Sample(object):
'''This class defines various methods related to the sample'''

    def __init__(self, thelist):
        self.list = thelist

    def draw_sample(self, samplesize):
        sample=random.sample(self.list,samplesize)
        return sample

choices=Sample(range(100))
print choices.draw_sample(5)

(Я также изменил условные обозначения в приведенном выше примере, чтобы они соответствовали стилю, рекомендованному PEP 8.)

Одним из преимуществ Python является то, что он не заставляет вас использовать классы для всего. Вы можете использовать их только тогда, когда есть данные или состояние, которые должны быть связаны с методами, для чего предназначены классы. В противном случае вы можете использовать функции, для которых они предназначены.


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

4
+1: Это было хорошим объяснением некоторых заблуждений ОП. И вы были очень честны, чтобы сказать заранее, что на самом деле вы не отвечали на его вопрос о статических методах, а скорее дали гораздо лучшее решение, заявив, что его проблема вообще не требует классов.
MestreLion

22

Зачем нужно определять статические методы ?

Предположим, у нас есть classвызываемый, Mathтогда

никто не хочет , чтобы создать объект , class Math
а затем вызывать методы , как ceilи floorи fabsна нем.

Итак, мы их делаем static.

Например, делая

>> Math.floor(3.14)

намного лучше чем

>> mymath = Math()
>> mymath.floor(3.14)

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

Почему не все методы определены как статические ?

У них нет доступа к переменным экземпляра.

class Foo(object):
    def __init__(self):
        self.bar = 'bar'

    def too(self):
        print self.bar

    @staticmethod
    def foo():
        print self.bar

Foo().too() # works
Foo.foo() # doesn't work

Вот почему мы не делаем все методы статическими.


10
Но почему не математика пакетов? У Python есть пакеты для этого, вам не нужно определение класса для создания пространства имен.
extraneon

6
@extraneon: Да, чувак, я знаю это, но я хотел иметь что-нибудь простое и знакомое для объяснения, поэтому я использовал Math. Вот почему я использовал заглавные буквы M.
Pratik Deoghare

6
OP не спрашивал, что такое статические методы. Он спросил, в чем их преимущество. Вы объясняете, как их использовать, а не насколько они полезны. В вашем конкретном примере пространство имен имело бы гораздо больше смысла.
Хавьер,

4
-1: Хорошее и правильное объяснение, но очень неудачно выбранный пример: Mathв первую очередь, нет причин для того, чтобы вы создавали класс, модуль был бы лучше. Попробуйте найти пример класса, который имеет смысл как класс, а не тот, из которого «никто не захочет создавать объект» .
MestreLion

3
А затем приведите пример допустимого варианта использования для одного (или некоторых ) методов классов, которые будут статическими, но не для всех . Если все методы класса статичны, класс должен быть модулем. Если ни один из них не статичен, значит, вы не отвечаете на вопрос.
MestreLion

13

Когда вы вызываете объект функции из экземпляра объекта, он становится «связанным методом» и получает сам объект экземпляра, который передается в качестве первого аргумента.

Когда вы звоните classmethod объект (который является оболочкой для объекта функции) в экземпляре объекта, класс объекта экземпляра передается в качестве первого аргумента.

Когда вы вызываете staticmethodобъект (который является оболочкой для объекта функции), неявный первый аргумент не используется.

class Foo(object):

    def bar(*args):
        print args

    @classmethod
    def baaz(*args):
        print args

    @staticmethod
    def quux(*args):
        print args

>>> foo = Foo()

>>> Foo.bar(1,2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
>>> Foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> Foo.quux(1,2,3)
(1, 2, 3)

>>> foo.bar(1,2,3)
(<Foo object at 0x1004a4510>, 1, 2, 3)
>>> foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> foo.quux(1,2,3)
(1, 2, 3)

3
+1: Наконец, отличное объяснение того, что такое статические методы и методы класса. Хотя вы не объяснили, зачем вообще нужно использовать статический метод, по крайней мере, вы четко объяснили на простом примере, что оба они намного лучше, чем официальные документы.
MestreLion

3

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

На сайте python есть отличная документация по статическим методам:
http://docs.python.org/library/functions.html#staticmethod


Спасибо, Дэвид. Но почему бы тогда не определить каждый метод как статический, поскольку они также работают с экземплярами. Есть ли у этого недостатки?
Curious2learn

4
@ Curious2learn: Нет, со статическими методами у вас нет доступа к экземпляру: экземпляр игнорируется, за исключением его класса.
Феликс Клинг,

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

1
@extraneon - это больше вопрос организационных предпочтений кода; наличие статических методов дает дополнительную возможность.
Чарльз Даффи

@Felix - Спасибо. Это объясняет, почему каждый метод не должен быть статическим.
Curious2learn

3

Альтернативы staticmethodявляются: classmethod, instancemethod, и function. Если вы не знаете, что это, прокрутите вниз до последнего раздела. Если a staticmethodлучше любой из этих альтернатив, зависит от того, для какой цели он написан.

преимущества статического метода Python

  • Если вам не нужен доступ к атрибутам или методам класса или экземпляра, staticmethodлучше, чем classmethodили instancemethod. Таким образом становится ясно (из @staticmethodдекоратора), что состояние класса и экземпляра не читается и не изменяется. Однако использование a functionделает это различие еще более четким (см. Недостатки).
  • Сигнатура вызова у a staticmethodтакая же, как у classmethodor instancemethod, а именно <instance>.<method>(<arguments>). Следовательно, его можно легко заменить одним из трех, если это понадобится позже или в производном классе. Вы не можете этого сделать с помощью простого function.
  • A staticmethodможно использовать вместо a, functionчтобы показать, что он субъективно принадлежит классу, и предотвратить конфликты пространств имен.

недостатки статического метода Python

  • Он не может получить доступ к атрибутам или методам экземпляра или класса.
  • Сигнатура вызова у a staticmethodтакая же, как у classmethodили instancemethod. Это маскирует тот факт, что staticmethodфактически не читает и не изменяет информацию об объекте. Это затрудняет чтение кода. Почему бы просто не использовать function?
  • A staticmethodсложно повторно использовать, если вам когда-либо понадобится вызвать его извне класса / экземпляра, в котором он был определен. Если есть потенциал для повторного использования, functionлучшим выбором будет a .
  • Используется staticmethodредко, поэтому людям, читающим код, который включает его, может потребоваться немного больше времени, чтобы прочитать его.

альтернативы статическому методу в Python

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

  • staticmethodПринадлежит к классу , но не может получить доступ или изменить любую информацию экземпляра или класса.

Этому есть три альтернативы:

  • У classmethodобъекта есть доступ к классу вызывающего.
  • У instancemethodобъекта есть доступ к экземпляру вызывающего и его классу.
  • Не functionимеет ничего общего с классами. По возможностям он наиболее близок к staticmethod.

Вот как это выглядит в коде:

# function
# has nothing to do with a class
def make_cat_noise(asker_name):
    print('Hi %s, mieets mieets!' % asker_name)

# Yey, we can make cat noises before we've even defined what a cat is!
make_cat_noise('JOey')  # just a function

class Cat:
    number_of_legs = 4

    # special instance method __init__
    def __init__(self, name):
        self.name = name

    # instancemethod
    # the instance (e.g. Cat('Kitty')) is passed as the first method argument
    def tell_me_about_this_animal(self, asker_name):
        print('Hi %s, This cat has %d legs and is called %s'
              % (asker_name, self.number_of_legs, self.name))

    # classmethod
    # the class (e.g. Cat) is passed as the first method argument
    # by convention we call that argument cls
    @classmethod
    def tell_me_about_cats(cls, asker_name):
        print("Hi %s, cats have %d legs."
              % (asker_name, cls.number_of_legs))
        # cls.name  # AttributeError because only the instance has .name
        # self.name  # NameError because self isn't defined in this namespace

    # staticmethod
    # no information about the class or the instance is passed to the method
    @staticmethod
    def make_noise(asker_name):
        print('Hi %s, meooow!' % asker_name)
        # class and instance are not accessible from here

# one more time for fun!
make_cat_noise('JOey')  # just a function

# We just need the class to call a classmethod or staticmethod:
Cat.make_noise('JOey')  # staticmethod
Cat.tell_me_about_cats('JOey')  # classmethod
# Cat.tell_me_about_this_animal('JOey')  # instancemethod -> TypeError

# With an instance we can use instancemethod, classmethod or staticmethod
mycat = Cat('Kitty')  # mycat is an instance of the class Cat
mycat.make_noise('JOey')  # staticmethod
mycat.tell_me_about_cats('JOey')  # classmethod
mycat.tell_me_about_this_animal('JOey')  # instancemethod

1

Потому что функции пространства имен хороши (как было указано ранее):

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

  2. Когда я реорганизую действительно прогнивший код, я начинаю с попытки создать как можно больше методов @staticmethod. Это позволяет мне затем извлечь эти методы в класс - хотя я согласен, я редко использую это, но несколько раз помогал.


1

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

Единственное, что оправдывает их существование, - это удобство. Статические методы распространены в других популярных языках программирования, так почему бы не использовать Python? Если вы хотите создать функцию с поведением, которое очень тесно связано с классом, для которого вы ее создаете, но на самом деле она не обращается / не изменяет внутренние данные экземпляра класса таким образом, который оправдывает концептуализацию его как типичного метод этого класса затем ударил@staticmethod над ним, и любой, кто читает ваш код, сразу узнает много о природе метода и его связи с классом.

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


0

Статические методы практически не имеют смысла в Python. Вы используете либо методы экземпляра, либо методы класса.

def method(self, args):
    self.member = something

@classmethod
def method(cls, args):
    cls.member = something

@staticmethod
def method(args):
    MyClass.member = something
    # The above isn't really working
    # if you have a subclass

Вы сказали «почти». Есть ли место, где они могут быть лучше альтернатив?
Хавьер,

@ Хавьер: Я не могу придумать ни одного, но, вероятно, он есть, иначе почему бы этот метод был включен в библиотеку Python?
Георг Шелли,

1
@Javier, @Georg: Вы очень верите, что в корпусе Python нет мусора.
Чарльз Мерриам,

-1: Ответ на этот вопрос не представляет какой - либо польза случая staticmethodили любое объяснение того , что такое classmethodили почему и как это «лучше» , чем статические.
MestreLion

@CharlesMerriam: Конечно, статические методы имеют свое применение, иначе они были бы исключены или устарели в Python 3 (где была удалена большая часть устаревшего мусора). В документации нет ни одного слова против, staticmethodподтверждающего ваше утверждение о несостоятельности или утверждение Георга, которое classmethodследует использовать вместо этого).
MestreLion
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.