В чем разница между @staticmethod и @classmethod? - PullRequest
3121 голосов
/ 26 сентября 2008

В чем разница между функцией, отмеченной @staticmethod, и функцией, отмеченной @classmethod?

Ответы [ 23 ]

2749 голосов
/ 03 ноября 2009

Возможно, пример кода поможет: обратите внимание на разницу в сигнатурах вызовов 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 просто возвращает хорошая функция ole без привязки аргументов. 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>
736 голосов
/ 26 сентября 2008

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

A classmethod , с другой стороны, это метод, который получает класс, к которому он был вызван, или класс экземпляра, к которому он был вызван, в качестве первого аргумента. Это полезно, когда вы хотите, чтобы метод был фабрикой для класса: поскольку он получает фактический класс, для которого он был вызван, в качестве первого аргумента, вы всегда можете создать экземпляр нужного класса, даже если задействованы подклассы. Посмотрите, например, как dict.fromkeys(), метод класса, возвращает экземпляр подкласса при вызове в подклассе:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 
126 голосов
/ 26 сентября 2008

В основном @classmethod создает метод, первым аргументом которого является класс, из которого он вызывается (а не экземпляр класса), @staticmethod не имеет никаких неявных аргументов.

86 голосов
/ 03 ноября 2009

Официальные документы по питону:

@ classmethod

Метод класса получает класс как неявный первый аргумент, как Метод экземпляра получает экземпляр. Чтобы объявить метод класса, используйте этот идиома:

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

Форма @classmethod является функцией декоратор - см. Описание определения функций в Function определения для деталей.

Это может быть вызвано либо в классе (например, C.f()) или в экземпляре (например, C().f()). Экземпляр игнорируется за исключением его класса. Если метод класса вызывается для производного класс, объект производного класса передан как подразумеваемый первый аргумент.

Методы класса отличаются от C ++ или статические методы Java. Если ты хочешь те, см staticmethod() в этом раздел.

@ STATICMETHOD

Статический метод не получает неявный первый аргумент. Чтобы объявить статический метод, используйте эту идиому:

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

Форма @staticmethod является функцией декоратор - см. Описание определения функций в Function определения для деталей.

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

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

65 голосов
/ 03 ноября 2009

Здесь небольшая статья по этому вопросу

@ staticmethod function - это не что иное, как функция, определенная внутри класса. Он вызывается без создания экземпляра класса первым. Это определение является неизменным посредством наследования.

@ classmethod функция также может вызываться без создания экземпляра класса, но ее определение следует за Sub-классом, а не родительским классом, через наследование. Это потому, что первый аргумент для функции @classmethod всегда должен быть cls (class).

54 голосов
/ 22 апреля 2016

Чтобы решить, использовать ли @ 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
47 голосов
/ 23 января 2015

В чем разница между @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. Это точно называется " метод экземпляра ". Когда используется метод экземпляра, он используется как частичная функция (в отличие от функции total, определенной для всех значений при просмотре в исходном коде), то есть при использовании первый из аргументов предопределен как экземпляр объект со всеми заданными атрибутами. Он имеет экземпляр объекта, связанный с ним, и он должен быть вызван из экземпляра объекта. Как правило, он будет обращаться к различным атрибутам экземпляра.

Например, это экземпляр строки:

', '

если мы используем метод экземпляра, 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'>

См. Исходный код pandas для других подобных примеров альтернативных конструкторов, а также посмотрите официальную документацию Python по classmethod и staticmethod.

29 голосов
/ 24 февраля 2012

@ декораторы были добавлены в python 2.4. Если вы используете python <2.4, вы можете использовать функции classmethod () и staticmethod (). </p>

Например, если вы хотите создать фабричный метод (функция, возвращающая экземпляр другой реализации класса в зависимости от того, какой аргумент он получает), вы можете сделать что-то вроде:

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)

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

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

28 голосов
/ 19 мая 2015

Думаю, лучше задать вопрос: «Когда бы вы использовали @classmethod vs @staticmethod?»

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

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

27 голосов
/ 10 октября 2017

Я начал изучать язык программирования на C ++, а затем на Java, а затем на Python, и поэтому этот вопрос меня тоже беспокоил, пока я не понял простое использование каждого из них.

Метод класса: В отличие от Java и C ++, Python не перегружен конструктором. И поэтому для достижения этого вы можете использовать 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

Теперь, если требование возникает там, где вам нужно создать класс, используя только одно имя, просто 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")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...