Різниця між статичним методом і класним методом


3579

Яка різниця між функцією, прикрашеною @staticmethodта функцією, прикрашеною @classmethod?


11
Статичні методи іноді краще виконувати як функції модуля на пітоні заради чистоти. За допомогою функції модуля простіше імпортувати лише потрібну функцію та запобігти непотрібності ". синтаксис (я дивлюся на вас Objective-C). методи класу мають більше використання, оскільки їх можна використовувати в поєднанні з поліморфізмом для створення функцій «заводського зразка». це тому, що методи класу отримують клас як неявний параметр.
FistOfFury

27
tl; dr >> у порівнянні зі звичайними методами, статичні методи та методи класу також можна отримати за допомогою класу, але на відміну від методів класу, статичні методи незмінні через успадкування.
imsrgadich

4
Пов’язана розмова Реймонда Хеттінгера на тему: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep

точніше youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 вам потрібен лише метод класу для альтернативних конструкторів. В іншому випадку ви можете використовувати staticmethod та отримати доступ до будь-якого атрибуту класу (через. / Dot) якось більш інформативним фактичним "CLASSNAME" замість cls, як у classmethod
Роберт

Відповіді:


3136

Може бути , трохи прикладів коду допоможе: Зверніть увагу на різницю в підписах викликів в foo, class_fooі static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Нижче наведено звичайний спосіб, який екземпляр об'єкта викликає метод. Екземпляр об'єкта, aнеявно передається як перший аргумент.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

За допомогою classmethods клас об'єктного примірника неявно передається як перший аргумент замість self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Ви також можете зателефонувати class_fooза допомогою класу. Насправді, якщо ви визначаєте щось як метод class, це, мабуть, тому, що ви маєте намір викликати це з класу, а не з екземпляра класу. A.foo(1)підняв би TypeError, але A.class_foo(1)працює чудово:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Одне використання людей для класових методів - це створення спадкових альтернативних конструкторів .


За допомогою статичних методів ні self(об'єкт об'єкта), ні cls(клас) неявно не передаються як перший аргумент. Вони поводяться як звичайні функції, за винятком того, що ви можете викликати їх з екземпляра або класу:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Статичні методи використовуються для групування функцій, які мають певний логічний зв’язок класу з класом.


fooце лише функція, але коли ви телефонуєте, a.fooви не просто отримуєте функцію, ви отримуєте "частково застосовану" версію функції з об'єктом об'єкта, aприв'язаним як перший аргумент до функції. fooочікує 2 аргументи, тоді a.fooяк очікує лише 1 аргумент.

aприв’язаний до foo. Саме це мається на увазі під терміном "пов'язаний" нижче:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

З a.class_foo, aне зв'язаний class_foo, скоріше клас Aприв’язаний class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Тут зі статичним методом, навіть якщо це метод, a.static_fooпросто повертається гарна 'ole функція без аргументів. static_fooочікує 1 аргумент, а також a.static_foo1 аргумент.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

І звичайно те саме відбувається, коли ви телефонуєте замість static_fooкласу A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>

182
Я не розумію, у чому суть використання статичного методу. ми можемо просто використовувати просту функцію поза класом.
Алькотт

372
@Alcott: Ви можете перенести функцію в клас, оскільки вона логічно належить до класу. У вихідному коді Python (наприклад, багатопроцесорна робота, черепаха, dist-пакети) він використовується для "приховування" однокреслених "приватних" функцій з простору імен модуля. Його використання, однак, високо зосереджено лише в декількох модулях - можливо, це свідчить про те, що це в основному стилістична річ. Хоча я не міг знайти жодного прикладу цього, це @staticmethodможе допомогти впорядкувати ваш код, перезавантажившись підкласами. Без нього у вас були б варіанти функцій, що плавають навколо в просторі імен модулів.
unutbu

14
... разом з деякими поясненнями про те, де і навіщо використовувати інстанції, класи або статичні методи. Ви не сказали про це жодного слова, але і ОП не запитували про це.
MestreLion

106
@Alcott: як сказано унутбу, статичні методи - це організаційна / стилістична особливість. Іноді модуль має багато класів, і деякі допоміжні функції логічно прив'язані до даного класу, а не до інших, тому має сенс не «забруднювати» модуль багатьма «вільними функціями», і краще використовувати статичний метод, ніж покладатися на поганий стиль змішування класів та функцій defs разом у коді, лише щоб показати, що вони "пов'язані"
MestreLion

4
Ще одне використання для @staticmethod- ви можете використовувати його для видалення крихти. Я реалізую мову програмування в Python - функції, визначені бібліотекою, використовують статичний executeметод, де визначені користувачем функції вимагають аргументи екземпляра (тобто тіло функції). Цей декоратор виключає попередження про "невикористаний параметр" у інспектора PyCharm.
tehwalrus

798

STATICMETHOD це метод , який нічого не знає про клас або примірнику він був викликаний. Він просто отримує аргументи, які були передані, жодного неявного першого аргументу. Це в принципі марно в Python - ви можете просто використовувати модульну функцію замість статичного методу.

Classmethod , з іншого боку, це метод , який отримує передається клас він був викликаний на або клас примірника він був викликаний, в якості першого аргументу. Це корисно, коли ви хочете, щоб метод був заводським для класу: оскільки він отримує фактичний клас, на який він був викликаний в якості першого аргументу, ви завжди можете інстанціювати потрібний клас, навіть якщо задіяні підкласи. Наприклад dict.fromkeys(), спостерігайте за тим , як , класний метод, повертає екземпляр підкласу, коли викликається підкласом:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 

713
Статичний метод не є марним - це спосіб введення функції до класу (тому що він логічно належить до нього), вказуючи при цьому, що він не потребує доступу до класу.
Тоні Мейєр

135
Отже, лише "в основному" марно. Така організація, як і введення залежності, є дійсним використанням статичних методів, але оскільки модулі, а не класи, як у Java, є основними елементами організації коду в Python, їх використання та корисність рідкісні.
Томас Вутерс

40
Що логічно у визначенні методу всередині класу, коли він не має нічого спільного ні з класом, ні з його екземплярами?
Бен Джеймс

106
Можливо, заради спадщини? Статичні методи можуть бути успадковані та замінені так само, як методи екземпляра та методи класу, і пошук працює як очікувалося (на відміну від Java). Статичні методи насправді не вирішуються статично, чи викликаються вони у класі чи екземплярі, тому єдиною різницею між класовим та статичним методами є неявний перший аргумент.
haridsv

80
Вони також створюють більш чистий простір імен і полегшують розуміння функції, яка має щось спільне з класом.
Імброндір

149

В основному @classmethodробить метод, першим аргументом якого є клас, з якого він викликається (а не екземпляр класу), @staticmethodне має неявних аргументів.


103

Офіційні документи python:

@classmethod

Метод класу отримує клас як неявний перший аргумент, подібно до того, як метод екземпляра отримує екземпляр. Щоб оголосити метод класу, використовуйте цю ідіому:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

@classmethodФорма є функцією декоратора - дивіться опис визначень функцій в термінах функцій для деталей.

Його можна викликати або на класі (наприклад C.f()), або на екземплярі (наприклад, C().f()). Екземпляр ігнорується за винятком класу. Якщо метод класу викликається для похідного класу, об'єкт похідного класу передається як перший мається на увазі аргумент.

Методи класу відрізняються від статичних методів C ++ або Java. Якщо ви хочете таких, дивіться staticmethod()у цьому розділі.

@staticmethod

Статичний метод не отримує неявного першого аргументу. Щоб оголосити статичний метод, використовуйте цю ідіому:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

@staticmethodФорма є функцією декоратора - дивіться опис визначень функцій в термінах функцій для деталей.

Його можна викликати або на класі (наприклад C.f()), або на екземплярі (наприклад, C().f()). Екземпляр ігнорується за винятком класу.

Статичні методи в Python схожі на ті, які знайдені в Java або C ++. Більш досконалу концепцію див. classmethod()У цьому розділі.


Чи не помилка в документах? Не повинно бути статичним методом: "І екземпляр, і його клас ігноруються." замість "Екземпляр ігнорується за винятком класу."?
mirek

Це може бути помилка вирізання та вставки, але строго кажучи, ви не можете викликати метод у класі, якщо ви ігноруєте клас.
Аарон Бентлі

76

Ось коротка стаття з цього питання

Функція @staticmethod - це не що інше, як функція, визначена всередині класу. Він може дзвонити без спочатку ідентифікації класу. Це визначення незмінне через спадкування.

@classmethod функція також може викликатись без ініціювання класу, але її визначення слід за підкласом, а не з батьківського класу шляхом успадкування. Це тому, що перший аргумент функції @classmethod завжди повинен бути cls (class).


1
Так це означає, що за допомогою статичного методу я завжди пов'язаний з класом Parent, а з classmethod я пов'язаний з класом, в якому я оголошую classmethod (у даному випадку підклас)?
Мохан Гулаті

7
Ні. Використовуючи статичний метод, ви зовсім не пов'язані; немає неявного першого параметра. Використовуючи classmethod, ви отримуєте як неявний перший параметр клас, на який ви викликали метод (якщо ви його викликали безпосередньо на класі), або клас екземпляра, на який ви викликали метод (якщо ви викликали його в екземплярі).
Метт Андерсон

6
Можна трохи розширити, щоб показати, що, маючи клас в якості першого аргументу, методи класу мають прямий доступ до інших атрибутів і методів класу, тоді як статичні методи цього не роблять (для цього їм потрібно буде кодувати MyClass.attr)
MestreLion

"Це визначення незмінне через спадкування." не має сенсу в Python, ви можете просто перекрити статичний метод.
cz

68

Щоб вирішити, чи використовувати @staticmethod чи @classmethod, вам слід заглянути всередину свого методу. Якщо ваш метод отримує доступ до інших змінних / методів у вашому класі, тоді використовуйте @classmethod . З іншого боку, якщо ваш метод не торкається жодних інших частин класу, тоді використовуйте @staticmethod.

class Apple:

    _counter = 0

    @staticmethod
    def about_apple():
        print('Apple is good for you.')

        # note you can still access other member of the class
        # but you have to use the class instance 
        # which is not very nice, because you have repeat yourself
        # 
        # For example:
        # @staticmethod
        #    print('Number of apples have been juiced: %s' % Apple._counter)
        #
        # @classmethod
        #    print('Number of apples have been juiced: %s' % cls._counter)
        #
        #    @classmethod is especially useful when you move your function to other class,
        #       you don't have to rename the class reference 

    @classmethod
    def make_apple_juice(cls, number_of_apples):
        print('Make juice:')
        for i in range(number_of_apples):
            cls._juice_this(i)

    @classmethod
    def _juice_this(cls, apple):
        print('Juicing %d...' % apple)
        cls._counter += 1

яка б була перевага classmethod та cls._counter проти staticmethod та Apple._counter
Роберт

1
cls._counterвсе ще було б cls._counterнавіть якщо код покласти в інший клас або змінити назву класу. Apple._counterє специфічним для Appleкласу; для іншого класу або коли ім'я класу буде змінено, вам потрібно буде змінити посилається клас.
kiamlaluno

52

Яка різниця між @staticmethod і @classmethod в Python?

Можливо, ви бачили код Python, подібний цьому псевдокоду, який демонструє підписи різних типів методів і надає докстринг для пояснення кожного:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

Нормальний метод екземпляра

Спочатку я поясню a_normal_instance_method. Це саме називається " метод примірника ". Якщо використовується метод екземпляра, він використовується як часткова функція (на відміну від загальної функції, визначеної для всіх значень при перегляді у вихідному коді), тобто при використанні перший із аргументів визначений як екземпляр об'єкта, з усіма його заданими ознаками. У нього є примірник об'єкта, прив'язаного до нього, і його потрібно викликати з екземпляра об'єкта. Зазвичай він отримує доступ до різних атрибутів екземпляра.

Наприклад, це примірник рядка:

', '

якщо ми використовуємо метод екземпляра joinв цій рядку, щоб приєднати інший ітерабельний, це, очевидно, є функцією екземпляра, крім того, що це функція списку ітерабелів ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Сполучені методи

Методи екземплярів можуть бути пов'язані за допомогою пунктирного пошуку для подальшого використання.

Наприклад, це пов'язує str.joinметод з ':'екземпляром:

>>> join_with_colons = ':'.join 

І пізніше ми можемо використовувати це як функцію, яка вже пов'язана з цим першим аргументом. Таким чином, він працює як часткова функція для екземпляра:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Статичний метод

Статичний метод не сприймає екземпляр як аргумент.

Це дуже схоже на функцію рівня модуля.

Однак функція рівня модуля повинна жити в модулі і спеціально імпортуватися в інші місця, де вона використовується.

Однак, якщо він приєднаний до об'єкта, він буде зручно слідувати за об'єктом і за допомогою імпорту та успадкування.

Приклад статичного методу str.maketransперенесено з stringмодуля в Python 3. Це робить таблицю перекладу, придатну для споживання str.translate. Це здається досить нерозумним при використанні з екземпляра рядка, як показано нижче, але імпорт функції з stringмодуля досить незграбний, і приємно мати можливість викликати його з класу, як уstr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

У python 2 вам потрібно імпортувати цю функцію з все менш корисного рядкового модуля:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Метод класу

Метод класу схожий на метод екземпляра тим, що він бере непрямий перший аргумент, але замість того, щоб взяти екземпляр, він бере клас. Часто вони використовуються як альтернативні конструктори для кращого семантичного використання, і це підтримуватиме успадкування.

Найбільш канонічним прикладом вбудованого методу є клас dict.fromkeys. Він використовується як альтернативний конструктор dict (добре підходить, коли ви знаєте, які ваші ключі і хочете для них значення за замовчуванням.)

>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}

Коли ми підкласи dict, ми можемо використовувати той же конструктор, який створює екземпляр підкласу.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Дивіться вихідний код панди для інших подібних прикладів альтернативних конструкторів, а також дивіться також офіційну документацію Python на classmethodта staticmethod.


43

Я почав вивчати мову програмування з C ++, а потім Java, а потім Python, і це питання також мене непокоїло, поки я не зрозумів просте використання кожного з них.

Метод класу: Python на відміну від Java та C ++ не має конструкторської перевантаження. І тому для досягнення цього ви могли б скористатися classmethod. Наступний приклад пояснить це

Давайте розглянемо у нас є Personклас , який приймає два аргументи first_nameі last_nameта створює екземпляр Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Тепер, якщо вимога приходить там, де вам потрібно створити клас, використовуючи лише одне ім’я, просто a first_name, ви не можете зробити щось подібне в Python.

Це призведе до помилки, коли ви спробуєте створити об’єкт (екземпляр).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Однак ви можете досягти того ж, використовуючи @classmethodвказане нижче

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Статичний метод: Це досить просто, він не пов'язаний з екземпляром або класом, і ви можете просто викликати це за допомогою імені класу.

Тож скажімо у наведеному вище прикладі, що вам потрібна перевірка, яка first_nameне повинна перевищувати 20 символів, ви можете просто зробити це.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

і ви могли просто зателефонувати за допомогою class name

Person.validate_name("Gaurang Shah")

2
Це старий пост, але більш пітонічним способом досягти того, щоб конструктор прийняв один або два аргументи, використовував би def __init__(self, first_name, last_name="")замість classmethod get_person. Також результат буде точно таким же в цьому випадку.
акарілімано

31

Я думаю, що краще питання "Коли ви використовуєте @classmethod vs @staticmethod?"

@classmethod дозволяє легко отримати доступ до приватних членів, які пов'язані з визначенням класу. це чудовий спосіб зробити одиночні або фабричні класи, які контролюють кількість екземплярів створених об'єктів.

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


31

@decorators додано в python 2.4 Якщо ви використовуєте python <2.4, ви можете використовувати функцію classmethod () та staticmethod ().

Наприклад, якщо ви хочете створити заводський метод (Функція, що повертає екземпляр іншої реалізації класу залежно від того, який аргумент він отримує), ви можете зробити щось на кшталт:

class Cluster(object):

    def _is_cluster_for(cls, name):
        """
        see if this class is the cluster with this name
        this is a classmethod
        """ 
        return cls.__name__ == name
    _is_cluster_for = classmethod(_is_cluster_for)

    #static method
    def getCluster(name):
        """
        static factory method, should be in Cluster class
        returns a cluster object for the given name
        """
        for cls in Cluster.__subclasses__():
            if cls._is_cluster_for(name):
                return cls()
    getCluster = staticmethod(getCluster)

Також зауважте, що це хороший приклад використання класного методу та статичного методу. Статичний метод явно належить до класу, оскільки він використовує кластер Кластер внутрішньо. Класовому методу потрібна лише інформація про клас, а не примірник об'єкта.

Ще однією перевагою перетворення _is_cluster_forметоду classmethod є те, що підклас може вирішити змінити його реалізацію, можливо, тому, що він є досить загальним і може обробляти більше ніж один тип кластеру, тому просто перевірити назву класу було б недостатньо.


28

Статичні методи:

  • Прості функції без аргументу.
  • Робота над атрибутами класу; не за атрибутами екземпляра.
  • Можна викликати через клас і екземпляр.
  • Для їх створення використовується вбудована функція staticmethod ().

Переваги статичних методів:

  • Він локалізує ім'я функції в класіскопі
  • Він переміщує код функції ближче до місця його використання
  • Більш зручно імпортувати порівняно з функціями на рівні модуля, оскільки кожен метод не повинен бути спеціально імпортований

    @staticmethod
    def some_static_method(*args, **kwds):
        pass

Методи занять:

  • Функції, які мають перший аргумент як ім'я класу.
  • Можна викликати через клас і екземпляр.
  • Вони створені за допомогою вбудованої функції classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass

22

@staticmethodпросто вимикає функцію за замовчуванням як дескриптор методу. classmethod загортає вашу функцію в контейнер, що викликається, який передає посилання на клас власності як перший аргумент:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

По суті, classmethodмає накладні витрати, але дає можливість отримати доступ до власного класу. В якості альтернативи я рекомендую використовувати метаклас і класти методи класу на цей метаклас:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

1
Один з можливих недоліків метакласу для цього, який мені відразу виникає, - це те, що ви не можете викликати classmethod безпосередньо на екземпляр. c = C(); c.foo()підвищує AttributeError, вам доведеться це зробити type(c).foo(). Це також може вважатися особливістю - я не можу придумати, чому ви хочете цього зробити.
Зал Аарона

20

Остаточне керівництво про те, як використовувати статичні, класичні чи абстрактні методи в Python, є хорошим посиланням на цю тему, і підсумуйте його наступним чином.

@staticmethodФункція - це не що інше, як функція, визначена всередині класу. Він може дзвонити без спочатку ідентифікації класу. Це визначення незмінне через спадкування.

  • Python не повинен інстанціювати прив’язаний метод для об'єкта.
  • Це полегшує читабельність коду, і це не залежить від стану самого об'єкта;

@classmethodфункцію також можна викликати без ініціалізації класу, але її визначення слідує за підкласом, а не батьківський клас, завдяки успадкуванню, може бути замінено підкласом. Це тому, що перший аргумент @classmethodфункції завжди повинен бути cls (class).

  • Заводські методи , які використовуються для створення примірника для класу, використовуючи, наприклад, якусь попередню обробку.
  • Статичні методи, що викликають статичні методи : якщо ви розділите статичні методи на кілька статичних методів, ви не повинні жорстко кодувати ім'я класу, а використовувати методи класу

Дякую @zangw - спадкова незмінність статичної функції є ключовою відмінністю, як здається
hard_working_ant

18

Тільки перший аргумент відрізняється :

  • звичайний метод: поточний об'єкт, якщо автоматично передається як (додатковий) перший аргумент
  • classmethod: клас поточного об'єкта автоматично передається як (додатковий) аргумент кулака
  • staticmethod: додаткові аргументи не передаються автоматично. Те, що ви перейшли до функції, - те, що отримаєте.

Більш детально ...

нормальний метод

Коли викликається метод об'єкта, йому автоматично надається додатковий аргумент selfяк його перший аргумент. Тобто метод

def f(self, x, y)

треба викликати з 2 аргументами. selfавтоматично передається, і це сам об'єкт .

класний метод

Коли метод прикрашений

@classmethod
def f(cls, x, y)

наданий автоматично аргумент не self , а клас self .

статичний метод

Коли метод прикрашений

@staticmethod
def f(x, y)

методу взагалі не надано жодного автоматичного аргументу. Дано лише ті параметри, з якими він викликається.

звичаї

  • classmethod в основному використовується для альтернативних конструкторів.
  • staticmethodне використовує стан об’єкта. Це може бути функція, зовнішня для класу. Він розміщується всередині класу лише для групування функцій з подібною функціональністю (наприклад, як Mathстатичні методи класу Java )
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)

17

Дозвольте мені розповісти схожість між методом, прикрашеним спочатку @classmethod vs @staticmethod.

Схожість: обох можна викликати на самому Класі , а не просто на екземпляр класу. Отже, обидва в певному сенсі - це методи Класу .

Різниця: класний метод отримає сам клас в якості першого аргументу, а статичний метод - ні.

Отже, статичний метод, в певному сенсі, не пов'язаний з самим Класом і просто зависає там тільки тому, що він може мати пов'язану функціональність.

>>> class Klaus:
        @classmethod
        def classmthd(*args):
            return args

        @staticmethod
        def staticmthd(*args):
            return args

# 1. Call classmethod without any arg
>>> Klaus.classmthd()  
(__main__.Klaus,)  # the class gets passed as the first argument

# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')

# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()  
()

# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)

11

Ще одне врахування щодо staticmethod vs classmethod стосується спадкування. Скажімо, у вас такий клас:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Тоді ви хочете перекрити bar()в дитячому класі:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Це працює, але зауважте, що тепер bar()реалізація в дочірньому класі ( Foo2) більше не може скористатися будь-яким конкретним для цього класу. Наприклад, скажіть, Foo2був метод, який називається, magic()який ви хочете використовувати при Foo2реалізації bar():

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"
    @staticmethod
    def magic():
        return "Something useful you'd like to use in bar, but now can't" 

Обхідний шлях тут буде дзвонити Foo2.magic()в bar(), але тоді ви повторивши себе (якщо ім'я Foo2зміни, ви повинні пам'ятати , щоб оновити цей bar()метод).

Для мене це незначне порушення принципу відкритого / закритого типу , оскільки прийняте рішення Fooвпливає на вашу здатність переробляти загальний код у похідному класі (тобто він менш відкритий для розширення). Якби ми bar()були, classmethodми б добре:

class Foo(object):
    @classmethod
    def bar(cls):
        return "In Foo"

class Foo2(Foo):
    @classmethod
    def bar(cls):
        return "In Foo2 " + cls.magic()
    @classmethod
    def magic(cls):
        return "MAGIC"

print Foo2().bar()

Дає: In Foo2 MAGIC


7

Спробую пояснити основну різницю на прикладі.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - ми можемо безпосередньо викликати статичні та класні методи без ініціалізації

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- Статичний метод не може викликати самометод, але може викликати інші статичні та класичні методи

3- Статичний метод належить до класу і взагалі не буде використовувати об'єкт.

4- Класовий метод пов'язаний не з об'єктом, а з класом.


оголошення 2: Ви впевнені? Як статичний метод може викликати клас-метод? На нього немає посилання (на його клас).
mirek

7

@classmethod: може використовуватися для створення спільного глобального доступу до всіх екземплярів, створених цього класу ..... як оновлення запису декількома користувачами .... Я, зокрема, знайшов, що він також використовує ful під час створення синглів.: )

@static метод: не має нічого спільного з класом або екземпляром, пов'язаним з ..., але для читабельності можна використовувати статичний метод


5

Ви можете розглянути різницю між:

Class A:
    def foo():  # no self parameter, no decorator
        pass

і

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Це змінилося між python2 та python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Таким чином, використання @staticmethodметодів, що викликаються лише безпосередньо з класу, стало необов'язковим у python3. Якщо ви хочете зателефонувати їм як з класу, так і з екземпляра, вам все одно потрібно використовувати @staticmethodдекоратор.

Інші випадки були добре висвітлені безвідмовною відповіддю.


5

Метод класу отримує клас як неявний перший аргумент, подібно до того, як метод екземпляра отримує екземпляр. Це метод, який пов'язаний з класом, а не об'єктом класу. Він має доступ до стану класу, оскільки він приймає параметр класу, який вказує на клас, а не на об’єкт об'єкта. Він може змінити стан класу, який застосовуватиметься до всіх примірників класу. Наприклад, він може змінити змінну класу, яка буде застосована до всіх примірників.

З іншого боку, статичний метод не отримує неявного першого аргументу, порівняно з методами класу або методами екземпляра. І не може отримати доступ або змінити стан класу. Він належить лише до класу, оскільки з точки зору дизайну це правильний шлях. Але з точки зору функціональності не пов'язаний, під час виконання, з класом.

в якості керівництва використовуйте статичні методи як утиліти, використовуйте методи класу, наприклад, як заводські. А може, визначити синглтон. І використовуйте методи екземпляра для моделювання стану та поведінки екземплярів.

Сподіваюся, я зрозумів!


4

Мій внесок демонструє різницю серед @classmethod, @staticmethodі методів екземпляра, в тому числі , як екземпляр може побічно називати @staticmethod. Але замість опосередкованого виклику @staticmethodз екземпляра, зробити його приватним може бути більш "пітонічним". Отримати щось із приватного методу тут не демонструється, але це в основному та сама концепція.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""

2

Методи класів, як випливає з назви, використовуються для внесення змін до класів, а не до об'єктів. Щоб внести зміни до класів, вони модифікують атрибути класу (а не об’єктні атрибути), оскільки саме так ви оновлюєте класи. Це є причиною того, що методи класу беруть клас (умовно позначається «cls») за перший аргумент.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Статичні методи, з іншого боку, використовуються для виконання функціональних можливостей, не пов'язаних з класом, тобто вони не будуть читати чи записувати змінні класу. Отже, статичні методи не сприймають класи як аргументи. Вони використовуються для того, щоб заняття могли виконувати функціональні можливості, безпосередньо не пов'язані з метою класу.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."

2

Проаналізуйте @staticmethod в буквальному сенсі, надаючи різні уявлення.

Нормальний метод класу - це неявний динамічний метод, який приймає екземпляр як перший аргумент.
Навпаки, статичний метод не приймає екземпляр як перший аргумент, тому його називають "статичним" .

Статичний метод дійсно є такою нормальною функцією, такою ж, як і поза визначенням класу.
Він, на щастя, згрупований у клас саме для того, щоб стояти ближче, де він застосований, або ви можете прокрутити його, щоб знайти його.


2

Я думаю, що надання чисто Python версії staticmethodта classmethodдопомогло б зрозуміти різницю між ними на мовному рівні.

Обидва вони є дескрипторами даних (їх було б простіше зрозуміти, якщо ви вперше ознайомитеся з дескрипторами ).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner

1

staticmethod не має доступу до атрибутів об'єкта, класу або батьківських класів в ієрархії спадкування. Його можна викликати в класі безпосередньо (без створення об’єкта).

classmethod не має доступу до атрибутів об'єкта. Однак він може отримати доступ до атрибутів класу та батьківських класів в ієрархії спадкування. Його можна викликати в класі безпосередньо (без створення об’єкта). Якщо викликати об'єкт, то це те саме, що і звичайний метод, який не має доступу self.<attribute(s)>та отримує доступ self.__class__.<attribute(s)>лише.

Подумайте, у нас є клас b=2, ми створимо об’єкт і відновимо це b=4в ньому. Staticmethod не може отримати доступ до нічого з попереднього. Класметод може отримати доступ .b==2лише через cls.b. Нормальний метод може отримати доступ як .b==4через, так self.bі .b==2через self.__class__.b.

Ми можемо дотримуватися стилю KISS (будьте простим, дурним): не використовуйте staticmethods та classmethods, не використовуйте класи без інстанції, отримуйте доступ лише до атрибутів об'єкта self.attribute(s). Є мови, де OOP реалізований таким чином, і я думаю, що це не погана ідея. :)


Ще одна важлива річ для classmethods: Якщо ви модифікуєте атрибут у методі class, всі існуючі об'єкти цього класу, які не встановлюють явно цей атрибут, матимуть змінене значення.
mirek

-4

Швидкий зліт інших ідентичних методів в iPython виявляє, що @staticmethodприносить граничний приріст продуктивності (у наносекундах), але в іншому випадку, здається, він не виконує жодної функції. Крім того, будь-які підвищення продуктивності, ймовірно, буде вимкнено додатковою роботою по обробці методу staticmethod()під час компіляції (що відбувається до будь-якого виконання коду при запуску сценарію).

Ради читабельності коду я б уникав, @staticmethodякщо ваш метод не буде використовуватися для навантажень роботи, де наносекунди рахуються.


7
"Інакше, здається, не виконує жодної функції": не зовсім вірно. Див. Вище обговорення.
Кіт Пінсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.