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


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


2675
2017-09-25 21:01


походження


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


Відповіді:


Може бути, трохи приклад коду допоможе: Зверніть увагу на різницю в підписів дзвінка 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)

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

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

Ви також можете зателефонувати class_foo використовуючи клас. Насправді, якщо ви визначите щось бути метод класу, це, мабуть, тому, що ви маєте намір називати його з класу, а не з екземпляра класу. 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 просто повертаєш хороша "оле-функція без пов'язаних аргументів. static_foo чекає 1 аргумент, і a.static_foo очікує 1 аргумент теж.

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

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

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

2320
2017-11-03 19:13



Я не розумію, що таке зловживання за використання статичного методу. ми можемо просто використовувати просту функцію зовнішнього класу. - Alcott
@Alcott: Можливо, ви захочете перенести функцію в клас, оскільки вона логічно належить до класу. У вихідному коді Python (наприклад, мультипроцесорні, черепахи, дистрибутиви) він використовується для "приховування" окремих "приватних" функцій з простору імен модулів. Його використання, однак, дуже сконцентроване в декількох модулях - можливо, це означає, що це, в основному, стилістична річ. Хоча я не міг знайти жодного прикладу цього, @staticmethod може допомогти організувати ваш код шляхом переоцінки підкласами. Без цього у вас є варіанти функції, що плаває навколо в області імен модулів. - unutbu
... разом з деякими поясненнями де і чому використовувати будь-який приклад, клас або статичні методи. Ви нічого не говорили про це, але про це О.П. не питала нічого. - MestreLion
@ Аlcott: як сказано в Unutbu, статичні методи є організацією / стилістичною функцією. Іноді модуль має багато класів, а деякі допоміжні функції логічно прив'язані до заданого класу, а не до інших, тому має сенс не "забруднювати" модуль багатьма "вільними функціями", а краще використовувати статичний метод, ніж покладатися на поганий стиль змішування класів і функція defs разом у коді тільки, щоб показати, вони "пов'язані" - MestreLion
@unutbu: Тим не менш, слід видалити цю цитату з Гвідо: це було з самого початку проекту нотаток про випуск python 2.2, і він був змінений у 2.2.3 на: However, class methods are still useful in other places, for example, to program inheritable alternate constructors.Навіть оригінальна цитата дуже вводить в оману, оскільки він лише сказав, що розподіл пітонів не використовується classmethod. Але тільки тому, що сам пітон не використовує дану функцію, це не означає, що це марна функція. Думай про складні числа або навіть СВРPython сам не може використовувати їх, але це далеко не даремно запропонувати - MestreLion


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

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

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

654
2017-09-25 21:05



Статичний метод не є марним - це спосіб поставити функцію в клас (оскільки вона логічно належить до нього), одночасно вказавши, що він не потребує доступу до класу. - Tony Meyer
Звідси тільки "в основному" марно. Така організація, а також ін'єкції залежностей, є доцільним використанням статичних методів, але оскільки модулі, а не класи, подібні до Java, є основними елементами організації коду в Python, їх використання та корисність рідкісні. - Thomas Wouters
Що логічно визначити метод у класі, коли він не має нічого спільного з класом або його екземплярами? - Ben James
Можливо, ради спадщини? Статичні методи можуть бути успадковані та переопределені, подібно до методів екземпляра та методів класу, і пошук виконується так, як очікувалося (на відміну від Java). Статичні методи насправді не вирішуються статично, чи викликаються в класі або екземплярі, тому єдиною відмінністю між класом і статичними методами є неявний перший аргумент. - haridsv
Вони також створюють простіший простір імен і полегшують розуміння функції, що мають відношення до класу. - Imbrondir


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


110
2017-09-25 21:07





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

@classmethod

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

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

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

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

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

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

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

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

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

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

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


76
2017-11-03 19:23





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

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

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


55
2017-11-03 19:02



Отже, це означає, що, використовуючи статичний метод, я завжди пов'язаний з класом батьківського класу, і з класним методом я пов'язаний класом, який я заявляю в методі класів (у цьому випадку, підклас)? - Mohan Gulati
Ні. За допомогою staticmethod ви не пов'язані зовсім; немає першого неявного параметра. За допомогою classmethod ви отримаєте як неявний перший параметр клас, на якому ви називали метод (якщо його назвав безпосередньо в класі) або клас екземпляра, до якого ви називали метод (якщо ви називали його в екземплярі). - Matt Anderson
Можна трохи розширити, щоб показати, що, маючи клас як перший аргумент, методи класу мають прямий доступ до інших атрибутів класу та методів, тоді як статичні методи не використовуються (для цього потрібно мати жорсткий код MyClass.attr) - MestreLion


Вирішити, чи варто використовувати @ статичний метод або @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

44
2018-04-22 15:40



Жаль, що ваш статичний метод насправді робить доступ до змінної класу. Можливо, ви поліпшите свій приклад. - Cilyan


Яка різниця між @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'

Метод класу

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

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


38
2018-01-23 20:01





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


27
2018-02-24 09:32





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

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

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


26
2018-05-19 15:27





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

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

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

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

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

Методи класу:

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

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

23
2017-10-03 10:41