Разница между статическим и классовым методами

3584

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

Дэрил Спитцер
источник
11
статические методы иногда лучше в качестве функций уровня модуля в python для чистоты. С помощью функции модуля проще импортировать только нужную функцию и избежать ненужных "." синтаксис (я смотрю на тебя 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
Robert Nowak

Ответы:

3138

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

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

И, конечно, то же самое происходит, когда вы звоните static_fooвместе с классом A.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>
unutbu
источник
182
Я не понимаю, в чем подвох использования статического метода. мы можем просто использовать простую функцию вне класса.
Олкотт
372
@Alcott: Вы можете захотеть переместить функцию в класс, потому что она логически принадлежит классу. В исходном коде Python (например, многопроцессорность, turtle, dist-packages) он используется для «скрытия» «подчеркивания» «частных» функций из пространства имен модуля. Его использование, тем не менее, сильно сконцентрировано в нескольких модулях - возможно, это указывает на то, что это в основном стилистическая вещь. Хотя я не смог найти ни одного примера этого, он @staticmethodможет помочь организовать ваш код, переопределяясь подклассами. Без этого у вас были бы варианты функции, плавающей в пространстве имен модуля.
unutbu
15
... вместе с некоторыми пояснениями о том, где и зачем использовать экземпляры, классы или статические методы. Вы не сказали ни слова об этом, но и ОП не спросил об этом.
MestreLion
106
@Alcott: как сказал unutbu, статические методы - это организационная / стилистическая особенность. Иногда модуль имеет много классов, и некоторые вспомогательные функции логически связаны с данным классом, а не с другими, поэтому имеет смысл не «загрязнять» модуль многими «свободными функциями», и лучше использовать статический чем полагаться на плохой стиль смешивания классов и определений функций в коде, чтобы показать, что они «связаны»
MestreLion
4
Еще одно использование для @staticmethod- вы можете использовать его для удаления Cruft. Я реализую язык программирования на Python - библиотечные функции используют статический executeметод, где пользовательские функции требуют аргументов экземпляра (т. Е. Тела функции). Этот декоратор устраняет предупреждения «неиспользуемый параметр self» в инспекторе PyCharm.
Техвалрус
799

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
>>> 
Томас Воутерс
источник
715
Статический метод не бесполезен - это способ помещения функции в класс (потому что он там логически принадлежит), при этом указывается, что он не требует доступа к классу.
Тони Мейер
136
Следовательно, только «в основном» бесполезно. Такая организация, а также внедрение зависимостей являются допустимым использованием статических методов, но поскольку модули, а не классы, как в Java, являются основными элементами организации кода в Python, их использование и полезность редки.
Томас Воутерс
40
Что логично в определении метода внутри класса, когда он не имеет ничего общего ни с классом, ни с его экземплярами?
Бен Джеймс
107
Возможно, ради наследства? Статические методы могут быть унаследованы и переопределены так же, как методы экземпляра и методы класса, и поиск работает должным образом (в отличие от Java). Статические методы на самом деле не разрешаются статически, независимо от того, вызваны ли они в классе или экземпляре, поэтому единственное отличие между классом и статическими методами - это неявный первый аргумент.
haridsv
80
Они также создают более чистое пространство имен и упрощают понимание того, как функция имеет отношение к классу.
Имброндир
149

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

Теренс Симпсон
источник
103

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

@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()В этом разделе.

Крис Б.
источник
Нет ли ошибки в документации? Не должно быть статического метода: «Экземпляр и его класс игнорируются». вместо «Экземпляр игнорируется, за исключением его класса.»?
Мирек
Это может быть ошибка вырезания и вставки, но, строго говоря, вы не можете вызвать метод класса, если игнорируете класс.
Аарон Бентли
76

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

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

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

Том Нейланд
источник
1
Значит ли это, что при использовании статического метода я всегда связан с классом Parent, а с методом класса я связан с классом, в котором я объявляю метод класса (в данном случае подкласс)?
Мохан Гулати
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
53

В чем разница между @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.

Аарон Холл
источник
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="")вместо метода класса get_person. Также результат будет точно таким же в этом случае.
akarilimano
31

Я думаю, что лучший вопрос: "Когда бы вы использовали @classmethod против @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)

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

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

Дженс Тиммерман
источник
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
Один из возможных недостатков метакласса, который сразу приходит мне в голову, заключается в том, что вы не можете вызывать метод класса непосредственно в экземпляре. c = C(); c.foo()поднимает AttributeError, вам придется сделать type(c).foo(). Это также может считаться особенностью - хотя я не могу понять, почему вы захотите.
Аарон Холл
20

Полное руководство по использованию статических, классовых или абстрактных методов в Python - одна из хороших ссылок на эту тему, и подытожьте ее следующим образом.

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

  • Python не должен создавать экземпляр метода привязки для объекта.
  • Это облегчает читабельность кода и не зависит от состояния самого объекта;

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

  • Фабричные методы , которые используются для создания экземпляра для класса, используя, например, некоторую предварительную обработку.
  • Статические методы, вызывающие статические методы : если вы разделяете статические методы на несколько статических методов, вам не нужно жестко кодировать имя класса, а использовать методы класса
zangw
источник
Спасибо @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)
blue_note
источник
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',)
Selva
источник
11

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

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

И затем вы хотите переопределить bar()в дочернем классе:

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

Это работает, но обратите внимание, что теперь bar()реализация в дочернем классе ( Foo2) больше не может использовать в своих интересах что-то определенное для этого класса. Например, у say 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 - мы можем напрямую вызывать методы static и classmethods без инициализации

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

2- Статический метод не может вызвать метод self, но может вызвать другой статический метод и метод класса

3- Статический метод принадлежит классу и не будет использовать объект вообще.

4- Метод класса связан не с объектом, а с классом.

Ризван Мумтаз
источник
Объявление 2: Вы уверены? Как статический метод может вызвать метод класса? Он не имеет ссылки на него (на свой класс).
Мирек
7

@classmethod: может использоваться для создания общего глобального доступа ко всем экземплярам, ​​созданным в этом классе ..... например, при обновлении записи несколькими пользователями .... В частности, я обнаружил, что это полезно при создании синглетонов ...: )

Метод @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декоратор.

Другие случаи были хорошо охвачены ответом unutbus.

Дэвид Шуман
источник
5

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

С другой стороны, статический метод не получает неявный первый аргумент по сравнению с методами класса или методами экземпляра. И не может получить доступ или изменить состояние класса. Он принадлежит только классу, потому что с точки зрения дизайна это правильный путь. Но с точки зрения функциональности не связан, во время выполнения, с классом.

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

Надеюсь, мне было ясно!

Николай Петридян
источник
4

Мой вклад демонстрирует разницу среди @classmethod, @staticmethodи методов экземпляра, в том числе , как экземпляр может косвенно называть @staticmethod. Но вместо того, чтобы косвенно вызывать a @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 буквально предоставляя различные идеи.

Обычный метод класса - это неявный динамический метод, который принимает экземпляр в качестве первого аргумента.
Напротив, метод static не принимает экземпляр в качестве первого аргумента, поэтому он называется «статическим» .

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

Исчисление
источник
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
Jacky1205
источник
1

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

classmethod не имеет доступа к атрибутам объекта. Однако он может обращаться к атрибутам класса и родительских классов в иерархии наследования. Он может быть вызван непосредственно в классе (без создания объекта). Если вызывается в объекте, то это то же самое, что и обычный метод, который не self.<attribute(s)>обращается и не обращается кself.__class__.<attribute(s)> только .

Думаю, у нас есть класс b=2, мы создадим объект и заново установим его b=4в нем. Статический метод не может получить доступ ни к чему из предыдущего. Classmethod может получить доступ .b==2только через cls.b. Нормальный метод может получить доступ как: .b==4через, так self.bи .b==2через self.__class__.b.

Мы могли бы следовать стилю KISS (пусть он будет простым, глупым): не используйте статические методы и методы классов, не используйте классы без их создания, обращайтесь только к атрибутам объекта self.attribute(s). Есть языки, где ООП реализован таким образом, и я думаю, что это неплохая идея. :)

Мирек
источник
Еще одна важная вещь для методов класса: если вы модифицируете атрибут в методе класса, все существующие объекты этого класса, которые явно не устанавливают этот атрибут, будут иметь измененное значение.
Мирек
-4

Быстрый взлом других идентичных методов в iPython показывает, что @staticmethodдает незначительный прирост производительности (в наносекундах), но в остальном он, похоже, не работает. Кроме того, любое повышение производительности, вероятно, будет устранено дополнительной работой по обработке метода staticmethod()во время компиляции (что происходит до выполнения любого кода при запуске сценария).

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

Катал Гарви
источник
7
«В противном случае, кажется, не выполняет никакой функции»: не совсем верно. Смотрите выше обсуждение.
Кит Пинсон