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

2216
голосов

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

задан Daryl Spitzer 26 сент. '08 в 0:01
источник

20 ответов

1882
голосов

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

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

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

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

a=A()

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

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

С помощью classmethods класс экземпляра объекта неявно передается как первый аргумент вместо self.

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

Вы также можете вызвать class_foo с помощью класса. Фактически, если вы определите что-то, что нужно classmethod, это, вероятно, потому, что вы намереваетесь вызывать его из класса, а не из экземпляра класса. A.foo(1) мог бы вызвать TypeError, но A.class_foo(1) работает просто отлично:

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

Одно из используемых людьми методов класса заключается в создании наследуемых альтернативных конструкторов.


С staticmethods ни один self (экземпляр объекта), ни cls (класс) неявно передаются в качестве первого аргумента. Они ведут себя как простые функции, за исключением того, что вы можете вызывать их из экземпляра или класса:

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

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

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


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

Здесь, с помощью staticmethod, хотя это метод, 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>
ответ дан unutbu 03 нояб. '09 в 22:13
источник
601
голос

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

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

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 
ответ дан Thomas Wouters 26 сент. '08 в 0:05
источник
80
голосов

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

ответ дан Terence Simpson 26 сент. '08 в 0:07
источник
61
голос

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

@classmethod

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

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

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

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

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

@staticmethod

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

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

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

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

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

ответ дан Chris B. 03 нояб. '09 в 22:23
источник
43
голосов

Здесь - короткая статья по этому вопросу

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

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

ответ дан Tom Neyland 03 нояб. '09 в 22:02
источник
33
голосов

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

Возможно, вы видели код Python, такой как этот псевдокод, который демонстрирует подписи различных типов методов и предоставляет докструмент для объяснения каждого из них:

class Foo(object):

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

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

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

Метод нормального экземпляра

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

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

', '

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

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

Связанные методы

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

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

>>> join_with_colons = ':'.join 

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

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

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

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

Он очень похож на функцию уровня модуля.

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

Если он привязан к объекту, он также будет удобно перемещаться по объекту посредством импорта и наследования.

Примером статического метода является str.maketrans, перемещенный из модуля string в Python 3. Он делает таблицу переводов подходящей для потребления str.translate. Это кажется довольно глупым при использовании из экземпляра строки, как показано ниже, но импорт функции из модуля string довольно неуклюжий, и приятно иметь возможность называть его из класса, как в str.maketrans

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

В python 2 вам нужно импортировать эту функцию из все более менее полезного строкового модуля:

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

Метод класса

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

Самый канонический пример встроенного класса - dict.fromkeys. Он используется как альтернативный конструктор dict (хорошо подходит для того, когда вы знаете, что у вас есть, и хотите по умолчанию для них.)

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

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

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

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

ответ дан Aaron Hall 23 янв. '15 в 23:01
источник
27
голосов

Чтобы решить, следует ли использовать @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
ответ дан Du D. 22 апр. '16 в 18:40
источник
24
голосов

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

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

class Cluster(object):

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

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

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

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

ответ дан Jens Timmerman 24 февр. '12 в 12:32
источник
22
голосов

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

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

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

ответ дан Nathan Tregillus 19 мая '15 в 18:27
источник
17
голосов

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

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

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

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

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

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

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

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    
ответ дан Preya 03 окт. '16 в 13:41
источник
15
голосов

@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'>
ответ дан Armin Ronacher 26 сент. '08 в 0:24
источник
12
голосов

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

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

ответ дан Tushar.PUCSD 13 дек. '15 в 22:37
источник
11
голосов

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

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

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

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

  • Factory, которые используются для создания экземпляра для класса, использующего, например, некоторую предварительную обработку.
  • Статические методы, вызывающие статические методы: если вы разбиваете статические методы несколькими статическими методами, вам не следует жестко кодировать имя класса, но использовать методы класса
ответ дан zangw 16 нояб. '15 в 5:00
источник
8
голосов

Другое соображение относительно метода staticmethod vs classmethod связано с наследованием. Скажем, у вас есть следующий класс:

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

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

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

Это работает, но обратите внимание, что теперь реализация bar() в дочернем классе (Foo2) больше не может использовать что-либо конкретное для этого класса. Например, скажем, Foo2 имел метод magic(), который вы хотите использовать в реализации Foo2 bar():

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

Обходной путь здесь состоял бы в вызове Foo2.magic() в bar(), но затем вы повторяетесь (если изменяется имя Foo2, вам нужно будет запомнить этот метод bar()).

Для меня это небольшое нарушение принципа open/closed, поскольку решение, принятое в Foo, влияет на вашу способность refactor common code в производном классе (т.е. он менее открыт для расширения). Если 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

ответ дан Adam Parkin 29 сент. '16 в 20:02
источник
5
голосов

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

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

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()

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

ответ дан Green Harry 06 июля '16 в 11:53
источник
5
голосов

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

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, но может вызывать другие статические и classmethod

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

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

ответ дан Rizwan Mumtaz 20 сент. '16 в 12:03
источник
1
голос

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

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

ответ дан vijay 20 сент. '17 в 19:57
источник
0
голосов

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

Метод класса: Python в отличие от Java и С++ не имеет перегрузки конструктора. Итак, чтобы достичь этого, вы можете использовать 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 (имя):   return len (name) <= 20

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

Person.validate_name("Gaurang Shah")
ответ дан Gaurang Shah 10 окт. '17 в 13:10
источник
-5
голосов
#!/usr/bin/python
#coding:utf-8

class Demo(object):
    def __init__(self,x):
        self.x = x

    @classmethod
    def addone(self, x):
        return x+1

    @staticmethod
    def addtwo(x):
        return x+2

    def addthree(self, x):
        return x+3

def main():
    print Demo.addone(2)
    print Demo.addtwo(2)

    #print Demo.addthree(2) #Error
    demo = Demo(2)
    print demo.addthree(2)


if __name__ == '__main__':
    main()
ответ дан Zuckonit 18 нояб. '13 в 8:53
источник
-7
голосов

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

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

ответ дан Cathal Garvey 17 дек. '12 в 21:51
источник

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