Когда мне следует использовать @classmethod, а когда def (self)?

88

При интеграции приложения Django, которое я раньше не использовал, я обнаружил два разных способа определения функций в классах. Автор, кажется, намеренно использует их оба. Первым я сам часто пользуюсь:

class Dummy(object):

    def some_function(self,*args,**kwargs):
        do something here
        self is the class instance

Другой - тот, которым я не пользуюсь, в основном потому, что не понимаю, когда его использовать и для чего:

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        do something here
        cls refers to what?

В документации Python classmethodдекоратор объясняется следующим предложением:

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

Так что, я полагаю, clsотносится к Dummyсебе (к class, а не к экземпляру). Я не совсем понимаю, почему это существует, потому что я всегда мог это сделать:

type(self).do_something_with_the_class

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

Marue
источник

Ответы:

70

Ваша догадка верна - вы понимаете, как это classmethod работает.

Причина в том, что эти методы можно вызывать как в экземпляре, так и в классе (в обоих случаях объект класса будет передан в качестве первого аргумента):

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        print cls

#both of these will have exactly the same effect
Dummy.some_function()
Dummy().some_function()

Об их использовании в экземплярах : существует как минимум два основных применения для вызова метода класса в экземпляре:

  1. self.some_function()вызовет версию для some_functionфактического типа self, а не для класса, в котором происходит этот вызов (и не потребует внимания, если класс будет переименован); а также
  2. В случаях, когда some_functionнеобходимо реализовать какой-либо протокол, но полезно вызвать только объект класса.

Разница сstaticmethod : Существует еще один способ определения методов, которые не имеют доступа к данным экземпляра, с именем staticmethod. Это создает метод, который вообще не получает неявный первый аргумент; соответственно, ему не будет передана никакая информация об экземпляре или классе, для которого он был вызван.

In [6]: class Foo(object): some_static = staticmethod(lambda x: x+1)

In [7]: Foo.some_static(1)
Out[7]: 2

In [8]: Foo().some_static(1)
Out[8]: 2

In [9]: class Bar(Foo): some_static = staticmethod(lambda x: x*2)

In [10]: Bar.some_static(1)
Out[10]: 2

In [11]: Bar().some_static(1)
Out[11]: 2

Основное применение, которое я нашел для этого, - адаптировать существующую функцию (которая не ожидает получения a self) как метод класса (или объекта).

Марчин
источник
2
Хороший: мне нравится, что ответ разбит на как - почему. Насколько я понял, @classmethod позволяет получить доступ к функции без необходимости в экземпляре. Это именно то, что я искал, спасибо.
marue
1
@marcin Настоящая утка придает ему другое измерение. Речь шла скорее о том, чтобы не писать такие вещи, как self.foo()положено Bar.foo().
Voo
5
@Voo На самом деле self.foo()предпочтительнее, потому что selfможет быть экземпляром подкласса, который реализует свой собственный foo.
Марцин
1
@Marcin Я думаю, это зависит от того, чего вы хотите достичь. Но я понимаю вашу точку зрения.
marue
1
Вы должны использовать другую лямбду для Bar, поскольку 1+1 == 2 == 1*2, поэтому по показанным результатам невозможно определить, что Bar().static_methodдействительно вызывается.
Джордж В. Рейли
0

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

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

Жоао Барбоза
источник
-3

Если вы добавите декоратор @classmethod, это означает, что вы собираетесь сделать этот метод статическим методом java или C ++. (статический метод - это общий термин, я думаю ;) ) Python также имеет @staticmethod. и разница между classmethod и staticmethod заключается в том, можете ли вы получить доступ к классу или статической переменной с помощью аргумента или самого имени класса.

class TestMethod(object):
    cls_var = 1
    @classmethod
    def class_method(cls):
        cls.cls_var += 1
        print cls.cls_var

    @staticmethod
    def static_method():
        TestMethod.cls_var += 1
        print TestMethod.cls_var
#call each method from class itself.
TestMethod.class_method()
TestMethod.static_method()

#construct instances
testMethodInst1 = TestMethod()    
testMethodInst2 = TestMethod()   

#call each method from instances
testMethodInst1.class_method()
testMethodInst2.static_method()

все эти классы увеличивают cls.cls_var на 1 и распечатывают его.

И все классы, использующие одно и то же имя в той же области видимости или экземпляры, созданные с помощью этого класса, будут использовать эти методы. Есть только один TestMethod.cls_var и еще один TestMethod.class_method (), TestMethod.static_method ()

И важный вопрос. зачем нужен этот метод.

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

Райан Ким
источник
2
(а) Статические методы - это нечто иное; (б) методы классов разделяются не всеми классами.
Marcin
@Marcin спасибо, что сообщил мне мои нечеткие определения и все такое.
Райан Ким