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



Answers

Статический метод - это метод, который ничего не знает о классе или экземпляре, на который он был вызван. Он просто получает аргументы, которые были переданы, не подразумевается первый аргумент. В 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
>>> 
Question

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




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

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

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




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

@classmethod

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

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

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

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

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

@staticmethod

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

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

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

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

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




@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



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

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

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 используются для группировки функций, которые имеют некоторое логическое соединение с классом класса.




Еще одно соображение относительно метода 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 , влияет на вашу способность реорганизовать общий код в производном классе (т.е. он менее открыт для расширения). Если 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




Analyze @staticmethod literally providing different insights.

A normal method of a class is an implicit dynamic method which takes the instance as first argument.
In contrast, a staticmethod does not take the instance as first argument, so is called 'static' .

A staticmethod is indeed such a normal function the same as those outside a class definition.
It is luckily grouped into the class just in order to stand closer where it is applied, or you might scroll around to find it.




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



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

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

Разница: класс-метод получит сам класс как первый аргумент, в то время как 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 ().

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

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

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

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

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

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



A quick hack-up ofotherwise identical methods in iPython reveals that @staticmethod yields marginal performance gains (in the nanoseconds), but otherwise it seems to serve no function. Also, any performance gains will probably be wiped out by the additional work of processing the method through staticmethod() during compilation (which happens prior to any code execution when you run a script).

For the sake of code readability I'd avoid @staticmethod unless your method will be used for loads of work, where the nanoseconds count.






Related