Коли слід використовувати @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

Це лише заради ясності, чи я пропустив найважливішу частину: моторошні та захоплюючі речі, які без цього не можна було зробити?

Відповіді:


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 дозволяє отримати доступ до функції без потреби в екземплярі. Це саме те, що я шукав, дякую.
Маре

1
@marcin Справжнє введення качки надає йому якийсь інший вимір. Йшлося більше про те, щоб не писати такі речі, як self.foo()тоді, коли це має бути Bar.foo().
Voo

5
@Voo Насправді self.foo()є кращим, оскільки це selfможе бути екземпляр підкласу, який реалізує свій власний foo.
Марсін

1
@Marcin Я думаю, це залежить від того, чого ти хочеш досягти. Але я розумію вашу думку.
Маре

1
Ви повинні використовувати іншу лямбду для Bar, тому що 1+1 == 2 == 1*2, так що за показаними результатами неможливо визначити, що Bar().static_methodнасправді називається.
George V. Reilly

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
(а) Статичні методи - це щось інше; (b) методами класів користуються не всі класи.
Марцін

@Marcin дякую, що повідомив мені мої незрозумілі визначення та все таке.
Райан Кім,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.