Kapan saya harus menggunakan @classmethod dan when def method (self)?

92

Saat mengintegrasikan aplikasi Django yang belum pernah saya gunakan sebelumnya, saya menemukan dua cara berbeda yang digunakan untuk mendefinisikan fungsi di kelas. Penulis sepertinya menggunakan keduanya dengan sangat sengaja. Yang pertama adalah yang sering saya gunakan:

class Dummy(object):

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

Yang lainnya adalah yang tidak saya gunakan, terutama karena saya tidak mengerti kapan harus menggunakannya, dan untuk apa:

class Dummy(object):

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

Dalam dokumen Python, classmethoddekorator dijelaskan dengan kalimat ini:

Metode kelas menerima kelas sebagai argumen pertama implisit, seperti metode instance menerima instance.

Jadi saya kira clsmengacu pada Dummydirinya sendiri (itu class, bukan contoh). Saya tidak begitu mengerti mengapa ini ada, karena saya selalu bisa melakukan ini:

type(self).do_something_with_the_class

Apakah ini hanya demi kejelasan, atau apakah saya melewatkan bagian terpenting: hal-hal menyeramkan dan menarik yang tidak dapat dilakukan tanpanya?

marue
sumber

Jawaban:

71

Tebakan Anda benar - Anda memahami cara classmethod kerjanya.

Alasannya adalah bahwa metode ini dapat dipanggil baik pada instance ATAU di kelas (dalam kedua kasus, objek kelas akan diteruskan sebagai argumen pertama):

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

Tentang penggunaan ini pada instance : Setidaknya ada dua kegunaan utama untuk memanggil metode kelas pada sebuah instance:

  1. self.some_function()akan memanggil versi some_functionon pada tipe sebenarnya self, bukan kelas tempat panggilan itu muncul (dan tidak perlu diperhatikan jika kelas diganti namanya); dan
  2. Dalam kasus di mana some_functiondiperlukan untuk mengimplementasikan beberapa protokol, tetapi berguna untuk memanggil objek kelas saja.

Perbedaannya denganstaticmethod : Ada cara lain untuk menentukan metode yang tidak mengakses data instance, disebut staticmethod. Itu menciptakan sebuah metode yang tidak menerima argumen pertama yang implisit sama sekali; oleh karena itu tidak akan diteruskan informasi apa pun tentang instance atau kelas tempat ia dipanggil.

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

Kegunaan utama yang saya temukan untuk itu adalah untuk mengadaptasi fungsi yang ada (yang tidak berharap menerima a self) menjadi metode pada kelas (atau objek).

Marcin
sumber
2
Bagus: Saya suka jawaban yang dipecah menjadi bagaimana - mengapa. Sejauh yang saya dapatkan sekarang @classmethod memungkinkan untuk mengakses fungsi tanpa perlu sebuah contoh. Inilah yang saya cari, terima kasih.
marue
1
@marcin Benar mengetik bebek memberikan dimensi lain. Ini lebih tentang tidak menulis hal-hal seperti self.foo()yang seharusnya Bar.foo().
Voo
5
@Voo Sebenarnya, self.foo()lebih disukai, karena selfmungkin merupakan turunan dari subkelas yang mengimplementasikan miliknya sendiri foo.
Marcin
1
@Marcin Saya pikir ini tergantung pada apa yang ingin Anda capai. Tapi saya mengerti maksud Anda.
Marue
1
Anda harus menggunakan lambda yang berbeda untuk Bar, sebagai 1+1 == 2 == 1*2, jadi tidak mungkin untuk membedakan dari hasil yang ditampilkan yang Bar().static_methodsebenarnya dipanggil.
George V. Reilly
0

Pada dasarnya, Anda harus menggunakan @classmethod saat Anda menyadari bahwa definisi metode tidak akan diubah atau diganti.

Tambahan: Secara teoritis, metode kelas lebih cepat daripada metode objek, karena tidak perlu instantiated dan membutuhkan lebih sedikit memori.

Joao Barbosa
sumber
-3

Jika Anda menambahkan dekorator @classmethod, itu berarti Anda akan menjadikan metode itu sebagai metode statis java atau C ++. (metode statis adalah istilah umum yang saya kira ;) ) Python juga memiliki @staticmethod. dan perbedaan antara classmethod dan staticmethod adalah apakah Anda dapat mengakses kelas atau variabel statis menggunakan argumen atau nama kelas itu sendiri.

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

semua kelas tersebut meningkatkan cls.cls_var sebesar 1 dan mencetaknya.

Dan setiap kelas yang menggunakan nama yang sama pada lingkup yang sama atau contoh yang dibangun dengan kelas ini akan berbagi metode tersebut. Hanya ada satu TestMethod.cls_var dan juga hanya ada satu TestMethod.class_method (), TestMethod.static_method ()

Dan pertanyaan penting. mengapa metode ini dibutuhkan.

classmethod atau staticmethod berguna saat Anda menjadikan kelas tersebut sebagai pabrik atau saat Anda harus menginisialisasi kelas hanya sekali. seperti membuka file sekali, dan menggunakan metode umpan untuk membaca file baris demi baris.

Ryan Kim
sumber
2
(a) Metode statis adalah sesuatu yang lain; (b) metode kelas tidak dibagikan oleh setiap kelas.
Marcin
@Marcin terima kasih telah memberi tahu saya definisi saya yang tidak jelas dan semua itu.
Ryan Kim