У чому перевага використання статичних методів у 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класу, екземпляри якого будуть локалями (duh). getAvailableLocales()Метод був би хорошим приклад методу статичного такого класу: це явно належить до класу Locale, а також явно не належить до якогось - або конкретного випадку.
MestreLion

... і це також не метод класу, оскільки йому може не знадобитися доступ до будь-якого з методів класів.
MestreLion

Редактор вказує на те, що статичний метод може отримати доступ до атрибутів класу, явно переходячи вниз з class_name.attribute, просто ні cls.attributeабо self.attribute. Це справедливо для атрибутів "public". За домовленістю, ви не повинні отримувати доступ до атрибутів, прихованих підкресленням, або імен, зіпсованих двома підкресленнями, таким чином. Це також означає, що ваш код буде більш крихким, коли атрибути зміщують місця в ієрархії успадкування.
Незвичайне мислення

Цитата з Гвідо, яка допомагає вирішити, коли використовувати staticmethods (ніколи): "Ми всі знаємо, наскільки обмежені статичні методи. (Вони в основному випадкові - ще в 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 насправді однакові причини).
Олексій

1
Найкраща відповідь на "чому статичний метод", але оригінальне питання також задається "чому всі методи не статичні?". Коротке "відсутність доступу до атрибутів екземпляра" охопило б і це.
Exu,

Єдиною справжньою перевагою IMO є те, що ви можете замінити його в підкласі, але навіть у цього є відчуття, що ви робите щось неправильно ООП, в якому випадку ви це зробите?
Борис Чурзін

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 для цього є пакети, для створення простору імен вам не потрібно визначення класу.
екстранеон

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 ви можете мати функції та функції статичного класу. Ця відповідь насправді не показує, чому вибрати статичний метод замість методу, а не в класі.
екстранеон

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

@Felix - Дякую. Це пояснює, чому кожен метод не повинен бути статичним методом.
Curious2learn

3

Альтернативи staticmethodє: classmethod, instancemethod, і function. Якщо ви не знаєте, що це, прокрутіть вниз до останнього розділу. Якщо a staticmethodкращий за будь-яку з цих альтернатив, залежить від того, з якою метою він написаний.

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

  • Якщо вам не потрібен доступ до атрибутів або методам класу або прикладу, staticmethodкраще , ніж classmethodабо instancemethod. Таким чином стає зрозумілим (з @staticmethodдекоратора), що стан класу та екземпляра не зчитується і не змінюється. Однак використання a functionробить це розмежування ще чіткішим (див. Недоліки).
  • Підпис дзвінка a staticmethodтакий самий, як підпис a classmethodабо instancemethod, а саме <instance>.<method>(<arguments>). Отже, його можна легко замінити одним із трьох, якщо це потрібно пізніше або в похідному класі. Ви не можете зробити це за допомогою простого function.
  • A staticmethodможна використовувати замість a, functionщоб зрозуміти, що він суб’єктивно належить до класу, та запобігти конфліктам простору імен.

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

  • Він не може отримати доступ до атрибутів або методів екземпляра або класу.
  • Підпис дзвінка a staticmethodтакий самий, як підпис дзвінка classmethodабо instancemethod. Це маскує той факт, що staticmethodфактично не читає та не змінює жодної інформації про об'єкт. Це ускладнює читання коду. Чому б просто не використовувати a 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над ним, і кожен, хто читає ваш код, відразу дізнається багато нового про природу методу та його зв'язок із класом.

Одне, що я час від часу люблю робити, - це помістити функціональність, яку мій клас багато використовує внутрішньо, у приватні @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

Ви сказали "майже". Чи є місце, де вони можуть бути кращими за альтернативи?
Хав'єр

@Javier: Я не можу придумати жодного, але, мабуть, є, чому б інакше цей метод був включений до бібліотеки Python?
Georg Schölly

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.