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


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


2588


источник


Ответы:


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

Здесь, используя статический метод, хотя это метод, 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>

2236



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

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

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

646



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


107



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

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


75



Вот это короткая статья по этому вопросу

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

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


54



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

42



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

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

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,


38



@decorators were added in python 2.4 If you're using python < 2.4 you can use the classmethod() and staticmethod() function.

For example, if you want to create a factory method (A function returning an instance of a different implementation of a class depending on what argument it gets) you can do something like:

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)

Also observe that this is a good example for using a classmethod and a static method, The static method clearly belongs to the class, since it uses the class Cluster internally. The classmethod only needs information about the class, and no instance of the object.

Another benefit of making the _is_cluster_for method a classmethod is so a subclass can decide to change it's implementation, maybe because it is pretty generic and can handle more than one type of cluster, so just checking the name of the class would not be enough.


27



I think a better question is "When would you use @classmethod vs @staticmethod?"

@classmethod allows you easy access to private members that are associated to the class definition. this is a great way to do singletons, or factory classes that control the number of instances of the created objects exist.

@staticmethod provides marginal performance gains, but I have yet to see a productive use of a static method within a class that couldn't be achieved as a standalone function outside the class.


25



Static Methods:

  • Simple functions with no self argument.
  • Work on class attributes; not on instance attributes.
  • Can be called through both class and instance.
  • The built-in function staticmethod()is used to create them.

Benefits of Static Methods:

  • It localizes the function name in the classscope
  • It moves the function code closer to where it is used
  • More convenient to import versus module-level functions since each method does not have to be specially imported

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

Class Methods:

  • Functions that have first argument as classname.
  • Can be called through both class and instance.
  • These are created with classmethod in-built function.

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

23



@staticmethod just disables the default function as method descriptor. classmethod wraps your function in a container callable that passes a reference to the owning class as first argument:

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

As a matter of fact, classmethod has a runtime overhead but makes it possible to access the owning class. Alternatively I recommend using a metaclass and putting the class methods on that metaclass:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>

19



The definitive guide on how to use static, class or abstract methods in Python is one good link for this topic, and summary it as following.

@staticmethod function is nothing more than a function defined inside a class. It is callable without instantiating the class first. It’s definition is immutable via inheritance.

  • Python does not have to instantiate a bound-method for object.
  • It eases the readability of the code, and it does not depend on the state of object itself;

@classmethod function also callable without instantiating the class, but its definition follows Sub class, not Parent class, via inheritance, can be overridden by subclass. That’s because the first argument for @classmethod function must always be cls (class).

  • Factory methods, that are used to create an instance for a class using for example some sort of pre-processing.
  • Static methods calling static methods: if you split a static methods in several static methods, you shouldn't hard-code the class name but use class methods

17