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

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

Ответы [ 23 ]

26 голосов
/ 03 октября 2016

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

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

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

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

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

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

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

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    
21 голосов
/ 26 сентября 2008

@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'>
19 голосов
/ 16 ноября 2015

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

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

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

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

  • Методы фабрики , которые используются для создания экземпляра для класса с использованием, например, некоторой предварительной обработки.
  • Статические методы, вызывающие статические методы : если вы разбили статические методы на несколько статических методов, вам не нужно жестко кодировать имя класса, а использовать методы класса
13 голосов
/ 14 января 2018

Позвольте мне рассказать о сходстве метода, украшенного @classmethod vs @staticmethod первым.

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

Разница: Метод класса получит сам класс в качестве первого аргумента, а метод static - нет.

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

>>> 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',)
10 голосов
/ 29 сентября 2016

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

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

8 голосов
/ 20 сентября 2016

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

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

5 голосов
/ 20 сентября 2017

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

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

3 голосов
/ 25 июня 2018

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

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

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.

1 голос
/ 12 декабря 2017

Анализ @staticmethod буквально , обеспечивающий различные идеи.

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

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

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