В чем разница между @staticmethod и @classmethod?

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

40 ответов

Решение

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

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

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

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

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

Чтобы решить, использовать ли @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

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

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() в этой секции.

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

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

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

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

Я начал изучать язык программирования на 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

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

и вы можете просто позвонить, используя имя класса

Person.validate_name("Gaurang Shah")

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

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

def f(self, x, y)

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

метод класса

Когда метод оформлен

@classmethod
def f(cls, x, y)

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

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

Когда метод оформлен

@staticmethod
def f(x, y)

метод не имеет никакого автоматического аргумента вообще. Дается только параметры, с которыми он вызывается.

обыкновений

  • classmethod в основном используется для альтернативных конструкторов.
  • staticmethod не использует состояние объекта. Это может быть функция, внешняя по отношению к классу. Он помещает внутрь класса только для группировки функций с аналогичной функциональностью (например, как Java Math класс статических методов)
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):
        return atan(y, x)


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

angle = Point.angle(3, 2)

Я думаю, что лучший вопрос: "Когда бы вы использовали @classmethod против @staticmethod?"

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

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

Статические методы:

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

Преимущества статических методов:

  • Локализует имя функции в класс
  • Он перемещает код функции ближе к месту его использования
  • Удобнее импортировать функции по сравнению с функциями уровня модуля, поскольку каждый метод не нужно специально импортировать

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

Методы класса:

  • Функции с первым аргументом в качестве имени класса.
  • Может вызываться как через класс, так и через экземпляр.
  • Они создаются с помощью встроенной функции classmethod.

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

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

Позвольте мне сначала рассказать о сходстве метода, украшенного @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',)

@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'>

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

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

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

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

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

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

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

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

@staticmethod означает: когда вызывается этот метод, мы не передаем ему экземпляр класса (как мы обычно делаем с методами). Это означает, что вы можете поместить функцию внутри класса, но не можете получить доступ к экземпляру этого класса (это полезно, когда ваш метод не использует экземпляр).

Разница возникает при наследовании.

Предположим, что есть два класса - родительский и дочерний. Если кто-то хочет использовать @staticmethod, метод print_name должен быть написан дважды, потому что имя класса должно быть написано в строке печати.

      class Parent:
   _class_name = "Parent"

   @staticmethod
   def print_name():
       print(Parent._class_name)


class Child(Parent):
   _class_name = "Child"

   @staticmethod
   def print_name():
       print(Child._class_name)


Parent.print_name()
Child.print_name()

Однако для @classmethod не требуется писать метод print_name дважды.

      class Parent:
    _class_name = "Parent"

    @classmethod
    def print_name(cls):
        print(cls._class_name)


class Child(Parent):
    _class_name = "Child"


Parent.print_name()
Child.print_name()

Я попытаюсь объяснить основную разницу на примере.

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- Метод класса связан не с объектом, а с классом.

Python поставляется с несколькими встроенными декораторами. Большая тройка:

      @classmethod
@staticmethod
@property

Декоратор @classmethod может быть вызван с экземпляром класса или непосредственно самим классом в качестве его первого аргумента.

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

Рассмотрим следующий класс:

      class DecoratorTest(object):

    def __init__(self):
        pass

    def doubler(self, x):
        print("running doubler")
        return x*2

    @classmethod
    def class_doubler(klass, x):
        print("running doubler: %s" % klass)
        return x*2

    @staticmethod
    def static_doubler(x):
        print("running quad")
        return x*2

decor = DecoratorTest()

Посмотрим, как это работает:

      print(decor.doubler(5))
# running doubler
# 10

print(decor.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10
print(DecoratorTest.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10

print(DecoratorTest.static_doubler(5))
# running doubler 
# 10
print(decor.static_doubler(5))
# running doubler 
# 10

print(decor.doubler)
# <bound method DecoratorTest.doubler of <__main__.DecoratorTest object at 0x7f90e74fd150>> 
print(decor.class_doubler)
# <bound method DecoratorTest.class_doubler of <class '__main__.DecoratorTest'>> 
print(decor.static_doubler)
# <function DecoratorTest.static_doubler at 0x7f90e7447440> 

Метод экземпляра:

+ Может изменять состояние экземпляра объекта

+ Может изменять состояние класса

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

- Невозможно изменить состояние экземпляра объекта

+ Может изменять состояние класса

Статический метод:

- Невозможно изменить состояние экземпляра объекта

- Невозможно изменить состояние класса

class MyClass:
    ''' 
    Instance method has a mandatory first attribute self which represent the instance itself. 
    Instance method must be called by a instantiated instance.
    '''
    def method(self):
        return 'instance method called', self
    
    '''
    Class method has a mandatory first attribute cls which represent the class itself. 
    Class method can be called by an instance or by the class directly. 
    Its most common using scenario is to define a factory method.
    '''
    @classmethod
    def class_method(cls):
        return 'class method called', cls
    
    '''
    Static method doesn’t have any attributes of instances or the class. 
    It also can be called by an instance or by the class directly. 
    Its most common using scenario is to define some helper or utility functions which are closely relative to the class.
    '''
    @staticmethod
    def static_method():
        return 'static method called'


obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()

вывод:

('instance method called', <__main__.MyClass object at 0x100fb3940>)
('class method called', <class '__main__.MyClass'>)
static method called

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

Статические методы используются для выполнения некоторых служебных задач, а методы классов используются для заводских методов. Фабричные методы могут возвращать объекты класса для различных вариантов использования.

И напоследок небольшой пример для лучшего понимания:

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

    @classmethod
    def get_from_string(cls, name_string: str):
        first_name, last_name = name_string.split()
        if Student.validate_name(first_name) and Student.validate_name(last_name):
            return cls(first_name, last_name)
        else:
            print('Invalid Names')

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


stackru_student = Student.get_from_string('Name Surname')
print(stackru_student.first_name) # Name
print(stackru_student.last_name) # Surname

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

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

Мой вклад демонстрирует разницу между @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)
'''
"""

Вы можете рассмотреть разницу между:

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.

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

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

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

Надеюсь, я был чист!

В Python метод класса получает класс в качестве неявного первого аргумента. Класс экземпляра объекта неявно передается в качестве первого аргумента. Это может быть полезно, когда кто-то хочет, чтобы метод был фабрикой класса, поскольку он получает фактический класс (который вызвал метод) в качестве первого аргумента, можно создать экземпляр правого класса, даже если это касается и подклассов.

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

class Test(object):
    def foo(self, a):
        print "testing (%s,%s)"%(self,a)

    @classmethod
    def foo_classmethod(cls, a):
        print "testing foo_classmethod(%s,%s)"%(cls,a)

    @staticmethod
    def foo_staticmethod(a):
        print "testing foo_staticmethod(%s)"%a

test = Test()

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

Методы класса, как следует из названия, используются для внесения изменений в классы, а не в объекты. Чтобы внести изменения в классы, они изменят атрибуты класса (не атрибуты объекта), поскольку именно так вы обновляете классы. По этой причине методы класса принимают класс (условно обозначаемый как "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."

Я думаю, что дать чисто 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

метод класса против статического метода в Python

Метод класса

Декоратор @classmethod - это встроенный декоратор функций, который является выражением, которое вычисляется после определения вашей функции. Результат этой оценки затеняет определение вашей функции.

Метод класса получает класс как неявный первый аргумент, так же, как метод экземпляра получает экземпляр

Синтаксис:

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

fun: function that needs to be converted into a class method
returns: a class method for function.
  • Метод класса - это метод, который привязан к классу, а не к объекту класса.
  • У них есть доступ к состоянию класса, так как он принимает параметр класса, который указывает на класс, а не на экземпляр объекта.
  • Он может изменять состояние класса, которое будет применяться ко всем экземплярам класса. Например, он может изменить переменную класса, которая будет применима ко всем экземплярам.

Статический метод

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

Синтаксис:

class C(object):
    @staticmethod
    def fun(arg1, arg2, ...):
        ...
returns: a static method for function fun.
  • Статический метод - это также метод, который связан с классом, а не с объектом класса.
  • Статический метод не может получить доступ или изменить состояние класса.
  • Он присутствует в классе, потому что имеет смысл, чтобы метод присутствовал в классе.

Метод класса против статического метода

  • Метод класса принимает cls в качестве первого параметра, в то время как статический метод не нуждается в особых параметрах.
  • Метод класса может получить доступ или изменить состояние класса, в то время как статический метод не может получить доступ или изменить его.
  • Мы используем декоратор @classmethod в python для создания метода класса и используем декоратор @staticmethod для создания статического метода в python.

Когда использовать что?

  • Обычно мы используем метод класса для создания фабричных методов. Методы фабрики возвращают объект класса (аналог конструктора) для разных вариантов использования.
  • Обычно мы используем статические методы для создания служебных функций.

Как определить метод класса и статический метод?

Чтобы определить метод класса в python, мы используем декоратор @classmethod, а для определения статического метода - декоратор @staticmethod.

Давайте посмотрим на пример, чтобы понять разницу между ними обоими. Допустим, мы хотим создать класс Person. Теперь Python не поддерживает перегрузку методов, таких как C++ или Java, поэтому мы используем методы класса для создания фабричных методов. В приведенном ниже примере мы используем метод класса для создания объекта person с года рождения.

Как объяснено выше, мы используем статические методы для создания служебных функций. В приведенном ниже примере мы используем статический метод, чтобы проверить, взрослый человек или нет.

Реализация

# Python program to demonstrate 
# use of class method and static method.
from datetime import date

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # a class method to create a Person object by birth year.
    @classmethod
    def fromBirthYear(cls, name, year):
        return cls(name, date.today().year - year)

    # a static method to check if a Person is adult or not.
    @staticmethod
    def isAdult(age):
        return age > 18

person1 = Person('mayank', 21)
person2 = Person.fromBirthYear('mayank', 1996)

print person1.age
print person2.age

# print the result
print Person.isAdult(22)

Выход

21
21
True

Ссылка

Сначала давайте начнем с примера кода, который мы будем использовать для понимания обеих концепций:

      class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

Метод класса

Метод класса принимает сам класс как неявный аргумент и - необязательно - любые другие аргументы, указанные в определении. Важно понимать, что метод класса не имеет доступа к экземплярам объекта (в отличие от методов экземпляра). Следовательно, методы класса не могут использоваться для изменения состояния экземпляра объекта, но вместо этого они могут изменять состояние класса, которое является общим для всех экземпляров этого класса. Методы класса обычно полезны, когда нам нужно получить доступ к самому классу - например, когда мы хотим создать фабричный метод, то есть метод, который создает экземпляры класса. Другими словами, методы класса могут служить альтернативными конструкторами.

В нашем примере кода экземпляр Employee может быть сконструирован с помощью трех аргументов; first_name , last_name и salary.

      employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

Теперь предположим, что есть вероятность, что имя Сотрудника может быть указано в одном поле, в котором имя и фамилия разделены пробелом. В этом случае мы могли бы использовать наш вызываемый метод класса, который принимает всего три аргумента. Первый - это сам класс, который является неявным аргументом, что означает, что он не будет предоставлен при вызове метода - Python автоматически сделает это за нас:

      employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

Обратите внимание, что также можно позвонить employee_from_full_name из экземпляров объекта, хотя в данном контексте это не имеет большого смысла:

      employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

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

      employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

Статические методы

С другой стороны, в статических методах ни экземпляр (т.е. self) ни сам класс (т.е. cls) передается как неявный аргумент. Это означает, что такие методы не могут получить доступ к самому классу или его экземплярам. Теперь можно утверждать, что статические методы бесполезны в контексте классов, поскольку они также могут быть помещены во вспомогательные модули вместо того, чтобы добавлять их в качестве членов класса. В объектно-ориентированном программировании важно структурировать классы в логические блоки, и поэтому статические методы весьма полезны, когда нам нужно добавить метод в класс просто потому, что он логически принадлежит классу. В нашем примере статический метод с именем get_employee_legal_obligations_txtпросто возвращает строку, содержащую юридические обязательства каждого сотрудника компании. Эта функция не взаимодействует ни с самим классом, ни с каким-либо экземпляром. Его можно было бы поместить в другой вспомогательный модуль, однако он имеет отношение только к этому классу, и поэтому мы должны поместить его в класс Employee.

К статическому методу можно получить доступ непосредственно из самого класса.

      print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

или из экземпляра класса:

      employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

Рекомендации

Другие вопросы по тегам