Pertanyaan Arti dari @classmethod dan @staticmethod untuk pemula?


Bisakah seseorang menjelaskan kepada saya arti dari @classmethod dan @staticmethod di python? Saya perlu tahu perbedaan dan artinya.

Sejauh yang saya mengerti, @classmethod memberitahu kelas bahwa itu adalah metode yang harus diwariskan ke dalam subclass, atau ... sesuatu. Namun, apa gunanya itu? Mengapa tidak mendefinisikan metode kelas tanpa menambahkan @classmethod atau @staticmethod atau apapun @ definisi?

tl; dr:  kapan haruskah saya menggunakannya, Mengapa haruskah saya menggunakannya, dan bagaimana haruskah saya menggunakannya?

Saya cukup maju dengan C ++, jadi menggunakan konsep pemrograman yang lebih canggih seharusnya tidak menjadi masalah. Jangan ragu memberi saya contoh C ++ yang sesuai jika memungkinkan.


1251
2017-08-29 13:37


asal


Jawaban:


Meskipun classmethod dan staticmethod cukup mirip, ada sedikit perbedaan dalam penggunaan untuk kedua entitas: classmethod harus memiliki referensi ke objek kelas sebagai parameter pertama, sedangkan staticmethod tidak dapat memiliki parameter sama sekali.

Contoh

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

Penjelasan

Mari kita asumsikan contoh kelas, berurusan dengan informasi tanggal (ini adalah apa yang akan menjadi boiler kami untuk memasak):

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

Kelas ini jelas dapat digunakan untuk menyimpan informasi tentang tanggal tertentu (tanpa informasi zona waktu; mari kita asumsikan semua tanggal disajikan dalam UTC).

Di sini kita punya __init__, penginisialisasi yang khas dari instance kelas Python, yang menerima argumen sebagai tipikal instancemethod, memiliki argumen non-pilihan pertama (self) yang memegang referensi ke instance yang baru dibuat.

Metode Kelas

Kami memiliki beberapa tugas yang dapat dilakukan dengan baik classmethods.

Mari kita berasumsi bahwa kita ingin menciptakan banyak Date instance kelas yang memiliki informasi tanggal berasal dari sumber luar yang dikodekan sebagai string format selanjutnya ('dd-mm-yyyy'). Kita harus melakukan itu di tempat yang berbeda dari kode sumber kami dalam proyek.

Jadi yang harus kita lakukan di sini adalah:

  1. Buat string untuk menerima hari, bulan, dan tahun sebagai tiga variabel bilangan bulat atau tupel 3 item yang terdiri dari variabel itu.
  2. Memberi contoh Date dengan meneruskan nilai-nilai tersebut ke panggilan inisialisasi.

Ini akan terlihat seperti:

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

Untuk tujuan ini, C ++ memiliki fitur seperti overloading, tetapi Python tidak memiliki fitur itu - jadi inilah saatnya classmethod berlaku. Ayo buat yang lain "konstruktor".

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

Mari kita lihat lebih teliti pada penerapan di atas, dan tinjau keuntungan apa yang kita miliki di sini:

  1. Kami telah menerapkan penguraian string tanggal di satu tempat dan dapat digunakan kembali sekarang.
  2. Enkapsulasi berfungsi dengan baik di sini (jika Anda berpikir bahwa Anda dapat menerapkan penguraian string sebagai fungsi tunggal di tempat lain, solusi ini cocok dengan paradigma OOP yang jauh lebih baik).
  3. cls adalah objek yang memegang kelas itu sendiri, bukan instance dari kelas. Ini sangat keren karena jika kita mewarisi kita Date kelas, semua anak akan memiliki from_string didefinisikan juga.

Metode statis

Bagaimana dengan staticmethod? Ini sangat mirip dengan classmethod tetapi tidak mengambil parameter wajib (seperti metode kelas atau metode instance).

Mari kita lihat kasus penggunaan selanjutnya.

Kami memiliki string tanggal yang kami ingin validasi entah bagaimana. Tugas ini juga terikat secara logis Date kelas yang telah kami gunakan sejauh ini, tetapi masih tidak membutuhkan Instansiasi itu.

Di sinilah tempatnya staticmethod bisa bermanfaat. Mari kita lihat potongan kode selanjutnya:

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

    # usage:
    is_date = Date.is_date_valid('11-09-2012')

Jadi, seperti yang bisa kita lihat dari penggunaan staticmethod, kita tidak memiliki akses apa pun kelasnya - ini pada dasarnya hanya sebuah fungsi, yang disebut sintaksis seperti metode, tetapi tanpa akses ke objek dan itu internal (bidang dan metode lain), sementara classmethod tidak.


2154
2017-08-29 14:03



Jawaban Rostyslav Dzinko sangat tepat. Saya pikir saya bisa menyoroti satu alasan lain yang harus Anda pilih @classmethod lebih @staticmethod ketika Anda membuat konstruktor tambahan.

Dalam contoh di atas, Rostyslav menggunakan @classmethod  from_string sebagai Pabrik untuk dibuat Date objek dari parameter yang tidak dapat diterima. Hal yang sama bisa dilakukan dengan @staticmethod seperti yang ditunjukkan pada kode di bawah ini:

class Date:
  def __init__(self, month, day, year):
    self.month = month
    self.day   = day
    self.year  = year


  def display(self):
    return "{0}-{1}-{2}".format(self.month, self.day, self.year)


  @staticmethod
  def millenium(month, day):
    return Date(month, day, 2000)

new_year = Date(1, 1, 2013)               # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object. 

# Proof:
new_year.display()           # "1-1-2013"
millenium_new_year.display() # "1-1-2000"

isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True

Dengan demikian keduanya new_year dan millenium_new_year adalah contoh Date kelas.

Tapi, jika Anda amati dengan saksama, proses Pabrik sulit untuk dibuat Date benda apa pun. Apa artinya ini adalah bahwa bahkan jika Date class adalah subclassed, subclass akan tetap membuat plain Date objek (tanpa properti apa pun dari subkelas). Lihat itu dalam contoh di bawah ini:

class DateTime(Date):
  def display(self):
      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)


datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False

datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class

datetime2 bukan merupakan contoh dari DateTime? WTF? Itu karena @staticmethod dekorator digunakan.

Dalam banyak kasus, ini tidak diinginkan. Jika apa yang Anda inginkan adalah metode Factory yang menyadari kelas yang memanggilnya, lalu @classmethod adalah apa yang kamu butuhkan.

Menulis ulang Date.millenium as (itulah satu-satunya bagian kode di atas yang berubah)

@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

memastikan bahwa class tidak dikodekan melainkan dipelajari. cls dapat berupa subkelas apa pun. Hasilnya object benar akan menjadi contoh cls. Mari kita uji itu.

datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True


datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"

Alasannya adalah, seperti yang Anda ketahui sekarang, @classmethod digunakan sebagai ganti @staticmethod


712
2018-01-30 13:35



@classmethod berarti: ketika metode ini dipanggil, kami meneruskan kelas sebagai argumen pertama, bukan instance dari kelas itu (seperti yang biasanya kita lakukan dengan metode). Ini berarti Anda dapat menggunakan kelas dan propertinya di dalam metode tersebut daripada contoh khusus.

@staticmethod berarti: ketika metode ini dipanggil, kita tidak melewatkan instance kelas untuk itu (seperti yang biasanya kita lakukan dengan metode). Ini berarti Anda dapat meletakkan fungsi di dalam kelas tetapi Anda tidak dapat mengakses instance dari kelas tersebut (ini berguna ketika metode Anda tidak menggunakan instance).


195
2017-08-29 13:40



@staticmethod fungsi tidak lebih dari fungsi yang didefinisikan di dalam kelas. Itu bisa dipanggil tanpa instantiate kelas pertama. Definisi ini tidak dapat diubah melalui pewarisan.

  • Python tidak harus memberi contoh metode terikat untuk objek.
  • Ini memudahkan pembacaan kode: melihat @staticmethod, kita tahu bahwa metode itu tidak tergantung pada keadaan objek itu sendiri;

@classmethod fungsi juga dapat dipanggil tanpa instantiate kelas, tetapi definisinya mengikuti kelas Sub, bukan kelas Parent, melalui warisan, dapat ditimpa oleh subclass. Itu karena argumen pertama untuk @classmethodfungsi harus selalu cls (class).

  • Metode pabrik, yang digunakan untuk membuat instance untuk kelas menggunakan misalnya semacam pra-pemrosesan.
  • Metode statis memanggil metode statis: jika Anda membagi metode statis dalam beberapa metode statis, Anda tidak harus mem-hard-kode nama kelas tetapi menggunakan metode kelas

sini adalah tautan yang bagus untuk topik ini.


53
2018-04-14 07:12



Satu akan digunakan @classmethod ketika dia ingin mengubah perilaku metode berdasarkan subkelas mana yang memanggil metode tersebut. ingat kita memiliki referensi ke kelas panggilan dalam metode kelas.

Saat menggunakan statis Anda ingin perilaku tetap tidak berubah di seluruh subclass

Contoh:

class Hero:

  @staticmethod
  def say_hello():
     print("Helllo...")

  @classmethod
  def say_class_hello(cls):
     if(cls.__name__=="HeroSon"):
        print("Hi Kido")
     elif(cls.__name__=="HeroDaughter"):
        print("Hi Princess")

class HeroSon(Hero):
  def say_son_hello(self):
     print("test  hello")



class HeroDaughter(Hero):
  def say_daughter_hello(self):
     print("test  hello daughter")


testson = HeroSon()

testson.say_class_hello() #Output: "Hi Kido"

testson.say_hello() #Outputs: "Helllo..."

testdaughter = HeroDaughter()

testdaughter.say_class_hello() #Outputs: "Hi Princess"

testdaughter.say_hello() #Outputs: "Helllo..."

27
2017-08-21 07:18



Kompilasi kecil

@staticmethod Cara untuk menulis metode di dalam kelas tanpa referensi ke objek yang dipanggil. Jadi tidak perlu melewati argumen implisit seperti diri atau cls. Ini ditulis persis sama bagaimana menulis di luar kelas, tetapi itu tidak ada gunanya di python karena jika Anda perlu mengenkapsulasi suatu metode di dalam kelas karena metode ini perlu menjadi bagian dari kelas @staticmethod yang datang berguna dalam kasus.

@classmethod Ini penting ketika Anda ingin menulis metode pabrik dan dengan atribut khusus ini dapat dilampirkan di kelas. Atribut ini dapat ditimpa di kelas yang diwariskan.

Perbandingan antara dua metode ini bisa seperti di bawah ini

Table


26
2017-07-19 16:43



Arti dari @classmethod dan @staticmethod?

  • Metode adalah fungsi dalam ruang nama objek, dapat diakses sebagai atribut.
  • Metode reguler (mis.) Mendapat contoh (biasanya kami menyebutnya self) sebagai argumen pertama yang implisit.
  • SEBUAH kelas metode mendapat kelas (biasanya kita menyebutnya cls) sebagai argumen pertama yang implisit.
  • SEBUAH statis metode tidak mendapatkan argumen pertama implisit (seperti fungsi biasa).

kapan saya harus menggunakannya, mengapa saya harus menggunakannya, dan bagaimana cara menggunakannya?

Kamu tidak perlu baik dekorator. Tetapi pada prinsip bahwa Anda harus meminimalkan jumlah argumen untuk fungsi (lihat Clean Coder), mereka berguna untuk melakukan hal itu.

class Example(object):

    def regular_instance_method(self):
        """A function of an instance has access to every attribute of that 
        instance, including its class (and its attributes.)
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_f(self)

    @classmethod
    def a_class_method(cls):
        """A function of a class has access to every attribute of the class.
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_g(cls)

    @staticmethod
    def a_static_method():
        """A static method has no information about instances or classes
        unless explicitly given. It just lives in the class (and thus its 
        instances') namespace.
        """
        return some_function_h()

Untuk kedua metode dan metode kelas, tidak menerima setidaknya satu argumen adalah TypeError, tetapi tidak memahami semantik argumen itu adalah kesalahan pengguna.

(Menetapkan some_function's, misalnya:

some_function_h = some_function_g = some_function_f = lambda x=None: x

dan ini akan berhasil.)

pencarian bertitik pada contoh dan kelas:

Pencarian bertitik pada suatu instance dilakukan dalam urutan ini - kami mencari:

  1. pendeskripsi data di ruang nama kelas (seperti properti)
  2. data dalam contoh __dict__
  3. pendeskripsi non-data dalam namespace kelas (metode).

Catatan, pencarian bertitik pada suatu instance dipanggil seperti ini:

instance = Example()
instance.regular_instance_method 

dan metode adalah atribut yang dapat dipanggil:

instance.regular_instance_method()

metode instan

Argumen, self, secara implisit diberikan melalui pencarian bertitik.

Anda harus mengakses metode instan dari instance kelas.

>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>

metode kelas

Argumen, cls, secara implisit diberikan melalui pencarian bertitik.

Anda dapat mengakses metode ini melalui instance atau kelas (atau subclass).

>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>

metode statis

Tidak ada argumen yang secara implisit diberikan. Metode ini berfungsi seperti fungsi yang didefinisikan (misalnya) pada namespace modul, kecuali dapat dicari

>>> print(instance.a_static_method())
None

Sekali lagi, kapan saya harus menggunakannya, mengapa saya harus menggunakannya?

Masing-masing secara progresif lebih ketat dalam informasi yang mereka berikan metode versus metode instan.

Gunakan mereka ketika Anda tidak membutuhkan informasi.

Ini membuat fungsi dan metode Anda lebih mudah untuk berpikir tentang dan tidak menarik.

Mana yang lebih mudah untuk dibicarakan?

def function(x, y, z): ...

atau

def function(y, z): ...

atau

def function(z): ...

Fungsi-fungsi dengan argumen yang lebih sedikit lebih mudah untuk dibicarakan. Mereka juga lebih mudah unittest.

Ini mirip dengan contoh, kelas, dan metode statis. Ingatlah bahwa ketika kita memiliki sebuah instance, kita juga memiliki kelasnya, sekali lagi, tanyakan pada diri sendiri, mana yang lebih mudah ditebak ?:

def an_instance_method(self, arg, kwarg=None):
    cls = type(self)             # Also has the class of instance!
    ...

@classmethod
def a_class_method(cls, arg, kwarg=None):
    ...

@staticmethod
def a_static_method(arg, kwarg=None):
    ...

Contoh yang dibangun

Berikut adalah beberapa contoh bawaan bawaan saya:

Itu str.maketrans metode statis adalah fungsi dalam string modul, tetapi jauh lebih nyaman untuk dapat diakses dari str ruang nama.

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

Itu dict.fromkeys metode kelas mengembalikan kamus baru yang digunakan dari kode kunci:

>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}

Ketika subkelas, kami melihat bahwa itu mendapat informasi kelas sebagai metode kelas, yang sangat berguna:

>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'> 

Saran saya - Kesimpulan

Gunakan metode statis ketika Anda tidak membutuhkan argumen kelas atau contoh, tetapi fungsi ini terkait dengan penggunaan objek, dan akan lebih mudah bagi fungsi untuk berada di ruang nama objek.

Gunakan metode kelas ketika Anda tidak memerlukan informasi instan, tetapi perlu informasi kelas mungkin untuk kelas lain atau metode statis, atau mungkin sendiri sebagai konstruktor. (Anda tidak akan meng-hardcode kelas sehingga subclass dapat digunakan di sini.)


18
2017-10-24 16:09



Saya seorang pemula di situs ini, saya telah membaca semua jawaban di atas, dan mendapatkan informasi yang saya inginkan. Namun, saya tidak punya hak untuk memberi suara. Jadi saya ingin memulai dengan StackOverflow dengan jawaban yang saya mengerti.

  • @staticmethod tidak membutuhkan diri atau cls sebagai parameter pertama dari metode ini
  • @staticmethod dan @classmethod fungsi dibungkus bisa disebut dengan variabel instan atau kelas
  • @staticmethod hiasan fungsi berdampak semacam 'sifat abadi' yang mewarisi subclass tidak dapat menimpa fungsi kelas dasar yang dibungkus oleh @staticmethod penghias.
  • @classmethod need cls (Class name, Anda dapat mengubah nama variabel jika Anda mau, tetapi tidak disarankan) sebagai parameter fungsi pertama
  • @classmethod selalu digunakan dengan cara subkelas, warisan subkelas dapat mengubah efek fungsi kelas dasar, yaitu @classmethod dibungkus fungsi kelas dasar dapat ditimpa oleh subclass yang berbeda.

1
2017-07-23 11:38



Cara yang sedikit berbeda untuk memikirkannya yang mungkin berguna bagi seseorang ... Metode kelas digunakan dalam superclass untuk menentukan bagaimana metode tersebut harus berperilaku ketika dipanggil oleh kelas anak yang berbeda. Metode statis digunakan ketika kita ingin mengembalikan hal yang sama terlepas dari kelas anak yang kita panggil.


0
2018-01-24 22:59



Singkatnya, @classmehtod mengubah metode normal menjadi metode pabrik.

Mari kita jelajahi dengan sebuah contoh:

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'

Tanpa metode @ class, Anda harus berusaha menciptakan instance satu per satu dan mereka scartted.

book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey

Seperti misalnya dengan @classmethod

class PythonBook:
    def __init__(self, name, author):
        self.name = name
        self.author = author
    def __repr__(self):
        return f'Book: {self.name}, Author: {self.author}'
    @classmethod
    def book1(cls):
        return cls('Learning Python', 'Mark Lutz')
    @classmethod
    def book2(cls):
        return cls('Python Think', 'Allen B Dowey')

Menguji:

In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey

Lihat? Instance berhasil dibuat di dalam definisi kelas dan dikumpulkan bersama-sama.

Kesimpulannya, dekorator @classmethod mengkonversi metode konvensional ke metode pabrik, Menggunakan metode kelas memungkinkan untuk menambahkan banyak konstruktor alternatif seperlunya.


0
2017-12-12 09:41