Perbedaan antara metode statis dan metode kelas

3583

Apa perbedaan antara fungsi yang didekorasi dengan yang @staticmethoddidekorasi @classmethod?

Daryl Spitzer
sumber
11
metode statis kadang-kadang lebih baik sebagai fungsi tingkat modul di python demi kebersihan. Dengan fungsi modul, lebih mudah untuk mengimpor hanya fungsi yang Anda butuhkan dan mencegah "" yang tidak perlu. sintaks (Saya melihat Anda Objective-C). metode kelas lebih banyak digunakan karena mereka dapat digunakan dalam kombinasi dengan polimorfisme untuk membuat fungsi "pola pabrik". ini karena metode kelas menerima kelas sebagai parameter implisit.
FistOfFury
27
tl; dr >> bila dibandingkan dengan metode normal, metode statis dan metode kelas juga dapat diakses menggunakan kelas tetapi tidak seperti metode kelas, metode statis tidak dapat diubah melalui pewarisan.
imsrgadich
4
Pembicaraan terkait oleh Raymond Hettinger tentang topik: youtube.com/watch?v=HTLu2DFOdTg
moooeeeep
youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 lebih tepat Anda hanya perlu metode metoda untuk konstruktor alternatif. Kalau tidak, Anda dapat menggunakan metode static dan mengakses atribut kelas apa pun (via. / Dot) dengan "CLASSNAME" yang sebenarnya lebih informatif daripada cls seperti di classmethod
Robert Nowak

Jawaban:

3138

Mungkin sedikit contoh kode akan membantu: Perhatikan perbedaan tanda tangan panggilan foo, class_foodan static_foo:

class A(object):
    def foo(self, x):
        print "executing foo(%s, %s)" % (self, x)

    @classmethod
    def class_foo(cls, x):
        print "executing class_foo(%s, %s)" % (cls, x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)" % x    

a = A()

Di bawah ini adalah cara biasa instance instance memanggil metode. Contoh objek,, asecara implisit disahkan sebagai argumen pertama.

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

Dengan classmethods , kelas instance objek secara implisit dilewatkan sebagai argumen pertama alih-alih self.

a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Anda juga dapat menelepon class_foomenggunakan kelas. Bahkan, jika Anda mendefinisikan sesuatu sebagai metode kelas, itu mungkin karena Anda bermaksud menyebutnya dari kelas daripada dari instance kelas. A.foo(1)akan mengangkat TypeError, tetapi A.class_foo(1)berfungsi dengan baik:

A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)

Satu kegunaan yang ditemukan orang untuk metode kelas adalah membuat konstruktor alternatif yang dapat diwariskan .


Dengan metode statis , baik self(objek contoh) maupun cls(kelas) secara implisit dilewatkan sebagai argumen pertama. Mereka berperilaku seperti fungsi sederhana kecuali bahwa Anda dapat memanggil mereka dari instance atau kelas:

a.static_foo(1)
# executing static_foo(1)

A.static_foo('hi')
# executing static_foo(hi)

Metode statis digunakan untuk mengelompokkan fungsi yang memiliki beberapa koneksi logis dengan kelas ke kelas.


foohanya sebuah fungsi, tetapi ketika Anda menelepon a.fooAnda tidak hanya mendapatkan fungsi, Anda mendapatkan versi "sebagian diterapkan" dari fungsi dengan instance instance objek aterikat sebagai argumen pertama ke fungsi. foomengharapkan 2 argumen, sementara a.foohanya mengharapkan 1 argumen.

aterikat foo. Itulah yang dimaksud dengan istilah "terikat" di bawah:

print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>

Dengan a.class_foo, atidak terikat class_foo, melainkan kelas Aterikat class_foo.

print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>

Di sini, dengan metode statis, meskipun metode, a.static_foohanya mengembalikan fungsi yang baik tanpa argumen terikat. static_foomengharapkan 1 argumen, dan a.static_foomengharapkan 1 argumen juga.

print(a.static_foo)
# <function static_foo at 0xb7d479cc>

Dan tentu saja hal yang sama terjadi ketika Anda menelepon static_foodengan kelas Asebagai gantinya.

print(A.static_foo)
# <function static_foo at 0xb7d479cc>
unutbu
sumber
182
Saya tidak mengerti apa gunanya menggunakan metode statis. kita bisa menggunakan fungsi sederhana di luar kelas.
Alcott
372
@Alcott: Anda mungkin ingin memindahkan fungsi ke kelas karena secara logis milik kelas. Dalam kode sumber Python (mis. Multiprosesor, penyu, paket-dist), ia digunakan untuk "menyembunyikan" fungsi tunggal-garis bawah "pribadi" dari namespace modul. Penggunaannya, bagaimanapun, sangat terkonsentrasi hanya dalam beberapa modul - mungkin indikasi bahwa itu terutama hal gaya. Meskipun saya tidak dapat menemukan contoh ini, @staticmethodmungkin membantu mengatur kode Anda dengan ditimpa oleh subclass. Tanpanya Anda akan memiliki varian fungsi yang melayang-layang di namespace modul.
unutbu
15
... bersama dengan beberapa penjelasan tentang di mana dan mengapa menggunakan metode instance, class atau static. Anda tidak memberikan sepatah kata pun tentang itu, tetapi OP juga tidak bertanya tentang hal itu.
MestreLion
106
@Alcott: seperti kata unutbu, metode statis adalah fitur organisasi / gaya. Kadang-kadang modul memiliki banyak kelas, dan beberapa fungsi pembantu secara logis terikat ke kelas yang diberikan dan bukan ke yang lain, sehingga masuk akal untuk tidak "mencemari" modul dengan banyak "fungsi bebas", dan lebih baik menggunakan statis metode daripada mengandalkan gaya yang buruk dari pencampuran kelas dan def fungsi bersama-sama dalam kode hanya untuk menunjukkan bahwa mereka "terkait"
MestreLion
4
Satu lagi penggunaan untuk @staticmethod- Anda dapat menggunakannya untuk menghapus cruft. Saya menerapkan bahasa pemrograman dengan Python - fungsi-fungsi yang ditentukan-perpustakaan menggunakan executemetode statis , di mana fungsi-fungsi yang ditentukan pengguna memerlukan argumen instance (yaitu, badan fungsi). Penghias ini menghilangkan peringatan "parameter diri yang tidak digunakan" di inspektur PyCharm.
tehwalrus
799

Metode statis adalah metode yang tidak tahu apa-apa tentang kelas atau instance yang dipanggilnya. Hanya mendapat argumen yang disahkan, tidak ada argumen pertama tersirat. Ini pada dasarnya tidak berguna dalam Python - Anda bisa menggunakan fungsi modul bukan metode statis.

Sebuah classmethod , di sisi lain, adalah metode yang akan lulus kelas itu dipanggil, atau kelas dari contoh itu disebut pada, sebagai argumen pertama. Ini berguna ketika Anda ingin metode menjadi pabrik untuk kelas: karena mendapatkan kelas aktual yang dipanggil sebagai argumen pertama, Anda selalu dapat membuat instance kelas yang tepat, bahkan ketika subclass terlibat. Perhatikan misalnya bagaimana dict.fromkeys()suatu metode kelas, mengembalikan sebuah instance dari subclass ketika dipanggil pada sebuah subclass:

>>> class DictSubclass(dict):
...     def __repr__(self):
...         return "DictSubclass"
... 
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>> 
Thomas Wouters
sumber
715
Metode statis tidak berguna - ini adalah cara menempatkan fungsi ke dalam kelas (karena secara logis ada di sana), sementara menunjukkan bahwa itu tidak memerlukan akses ke kelas.
Tony Meyer
136
Karenanya hanya 'pada dasarnya' tidak berguna. Organisasi seperti itu, juga injeksi ketergantungan, adalah penggunaan metode statis yang valid, tetapi karena modul, bukan kelas seperti di Jawa, adalah elemen dasar dari organisasi kode dengan Python, penggunaan dan kegunaannya jarang terjadi.
Thomas Wouters
40
Apa yang logis tentang mendefinisikan metode di dalam kelas, ketika itu tidak ada hubungannya dengan kelas atau instansnya?
Ben James
107
Mungkin demi warisan? Metode statis dapat diwarisi dan diganti seperti metode instance dan metode kelas dan pencarian berfungsi seperti yang diharapkan (tidak seperti di Jawa). Metode statis tidak benar-benar diselesaikan secara statis apakah dipanggil pada kelas atau contoh, sehingga satu-satunya perbedaan antara metode kelas dan statis adalah argumen pertama yang tersirat.
haridsv
80
Mereka juga membuat namespace yang lebih bersih, dan membuatnya lebih mudah untuk memahami fungsi yang ada hubungannya dengan kelas.
Imbrondir
149

Pada dasarnya @classmethodmembuat metode yang argumen pertamanya adalah kelas dari mana (bukan dari instance kelas), @staticmethodtidak memiliki argumen implisit.

Terence Simpson
sumber
103

Dokumen python resmi:

@classmethod

Metode kelas menerima kelas sebagai argumen pertama implisit, seperti metode instance menerima instance. Untuk mendeklarasikan metode kelas, gunakan idiom ini:

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

The @classmethodbentuk adalah fungsi dekorator - lihat deskripsi definisi fungsi dalam Fungsi definisi untuk rincian.

Itu dapat dipanggil baik di kelas (seperti C.f()) atau pada instance (seperti C().f()). Instance diabaikan kecuali untuk kelasnya. Jika metode kelas dipanggil untuk kelas turunan, objek kelas turunan dilewatkan sebagai argumen pertama yang tersirat.

Metode kelas berbeda dari metode C ++ atau Java statis. Jika Anda menginginkannya, lihat staticmethod()di bagian ini.

@staticmethod

Metode statis tidak menerima argumen pertama implisit. Untuk mendeklarasikan metode statis, gunakan idiom ini:

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

The @staticmethodbentuk adalah fungsi dekorator - lihat deskripsi definisi fungsi dalam Fungsi definisi untuk rincian.

Itu dapat dipanggil baik di kelas (seperti C.f()) atau pada instance (seperti C().f()). Instance diabaikan kecuali untuk kelasnya.

Metode statis dengan Python mirip dengan yang ditemukan di Java atau C ++. Untuk konsep yang lebih maju, lihat classmethod()di bagian ini.

Chris B.
sumber
Apakah tidak ada kesalahan dalam dokumen? Seharusnya tidak di metode statis: "Instance dan kelasnya keduanya diabaikan." bukannya "Instance diabaikan kecuali untuk kelasnya."?
mirek
Ini mungkin kesalahan cut-and-paste, tetapi secara tegas Anda tidak bisa memanggil metode di kelas jika Anda mengabaikan kelas.
Aaron Bentley
76

Berikut adalah artikel singkat tentang pertanyaan ini

@staticmethod function tidak lebih dari fungsi yang didefinisikan di dalam kelas. Itu bisa dipanggil tanpa membuat instance kelas terlebih dahulu. Definisi itu tidak dapat diubah melalui pewarisan.

Fungsi @classmethod juga dapat dipanggil tanpa membuat instance kelas, tetapi definisinya mengikuti Sub kelas, bukan kelas Induk, melalui pewarisan. Itu karena argumen pertama untuk fungsi @classmethod harus selalu cls (class).

Tom Neyland
sumber
1
Jadi apakah itu berarti bahwa dengan menggunakan metode statis saya selalu terikat ke kelas Induk dan dengan metode class saya terikat kelas yang saya mendeklarasikan classmethod dalam (dalam hal ini sub kelas)?
Mohan Gulati
7
Tidak. Dengan menggunakan metode statis Anda tidak terikat sama sekali; tidak ada parameter pertama implisit. Dengan menggunakan classmethod, Anda mendapatkan parameter pertama implisit pada kelas tempat Anda memanggil metode (jika Anda memanggilnya langsung di kelas), atau kelas instance yang Anda panggil metode (jika Anda memanggilnya pada instance).
Matt Anderson
6
Dapat diperluas sedikit untuk menunjukkan bahwa, dengan memiliki kelas sebagai argumen pertama, metode kelas memiliki akses langsung ke atribut dan metode kelas lainnya, sedangkan metode statis tidak (mereka perlu hardcode MyClass.attr untuk itu)
MestreLion
"Definisi itu tidak dapat diubah melalui pewarisan." tidak masuk akal dengan Python, Anda bisa mengganti metode statis dengan baik.
cz
68

Untuk memutuskan apakah akan menggunakan @staticmethod atau @classmethod, Anda harus melihat ke dalam metode Anda. Jika metode Anda mengakses variabel / metode lain di kelas Anda maka gunakan @classmethod . Di sisi lain, jika metode Anda tidak menyentuh bagian lain dari kelas maka gunakan @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
Du D.
sumber
apa yang akan menjadi keuntungan dari classmethod dan cls._counter vs staticmethod dan Apple._counter
Robert Nowak
1
cls._countermasih akan menjadi cls._counterbahkan jika kode tersebut diletakkan di kelas yang berbeda, atau nama kelas diubah. Apple._counterkhusus untuk Applekelas; untuk kelas yang berbeda, atau ketika nama kelas diubah, Anda perlu mengubah kelas yang direferensikan.
kiamlaluno
53

Apa perbedaan antara @staticmethod dan @classmethod dalam Python?

Anda mungkin telah melihat kode Python seperti pseudocode ini, yang menunjukkan tanda tangan dari berbagai jenis metode dan menyediakan docstring untuk menjelaskan masing-masing:

class Foo(object):

    def a_normal_instance_method(self, arg_1, kwarg_2=None):
        '''
        Return a value that is a function of the instance with its
        attributes, and other arguments such as arg_1 and kwarg2
        '''

    @staticmethod
    def a_static_method(arg_0):
        '''
        Return a value that is a function of arg_0. It does not know the 
        instance or class it is called from.
        '''

    @classmethod
    def a_class_method(cls, arg1):
        '''
        Return a value that is a function of the class and other arguments.
        respects subclassing, it is called with the class it is called from.
        '''

Metode Instance Normal

Pertama saya akan jelaskan a_normal_instance_method. Ini tepatnya disebut " metode contoh ". Ketika metode instance digunakan, digunakan sebagai fungsi parsial (sebagai lawan dari fungsi total, didefinisikan untuk semua nilai bila dilihat dalam kode sumber) yaitu, ketika digunakan, argumen pertama ditentukan sebelumnya sebagai instance dari objek, dengan semua atribut yang diberikannya. Ia memiliki instance dari objek yang terikat padanya, dan itu harus dipanggil dari instance objek. Biasanya, itu akan mengakses berbagai atribut instance.

Misalnya, ini adalah instance dari string:

', '

jika kita menggunakan metode contoh, joinpada string ini, untuk bergabung dengan yang lain iterable, itu cukup jelas adalah fungsi dari contoh, selain menjadi fungsi dari daftar iterable, ['a', 'b', 'c']:

>>> ', '.join(['a', 'b', 'c'])
'a, b, c'

Metode terikat

Metode instan dapat diikat melalui pencarian bertitik untuk digunakan nanti.

Misalnya, ini mengikat str.joinmetode ke ':'instance:

>>> join_with_colons = ':'.join 

Dan nanti kita bisa menggunakan ini sebagai fungsi yang sudah memiliki argumen pertama terikat padanya. Dengan cara ini, ini berfungsi seperti fungsi parsial pada instance:

>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'

Metode Statis

Metode statis tidak mengambil contoh sebagai argumen.

Ini sangat mirip dengan fungsi level modul.

Namun, fungsi level modul harus hidup dalam modul dan diimpor secara khusus ke tempat lain di mana ia digunakan.

Namun, jika itu melekat pada objek, ia akan mengikuti objek dengan nyaman melalui impor dan pewarisan juga.

Contoh metode statis adalah str.maketrans, dipindahkan dari stringmodul dengan Python 3. Itu membuat tabel terjemahan cocok untuk dikonsumsi oleh str.translate. Tampaknya agak konyol ketika digunakan dari instance string, seperti yang ditunjukkan di bawah ini, tetapi mengimpor fungsi dari stringmodul agak canggung, dan senang bisa menyebutnya dari kelas, seperti padastr.maketrans

# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}

Dalam python 2, Anda harus mengimpor fungsi ini dari modul string yang semakin tidak berguna:

>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'

Metode Kelas

Metode kelas mirip dengan metode instance dalam mengambil argumen pertama implisit, tetapi alih-alih mengambil instance, dibutuhkan kelas. Seringkali ini digunakan sebagai konstruktor alternatif untuk penggunaan semantik yang lebih baik dan akan mendukung warisan.

Contoh paling kanonik dari metode kelas builtin adalah dict.fromkeys. Ini digunakan sebagai konstruktor alternatif dict, (cocok untuk ketika Anda tahu apa kunci Anda dan ingin nilai default untuk mereka.)

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

Ketika kita subclass dict, kita dapat menggunakan konstruktor yang sama, yang membuat turunan dari subclass.

>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>

Lihat kode sumber panda untuk contoh serupa lainnya dari konstruktor alternatif, dan lihat juga dokumentasi Python resmi pada classmethoddan staticmethod.

Aaron Hall
sumber
43

Saya mulai belajar bahasa pemrograman dengan C ++ dan kemudian Java dan kemudian Python dan pertanyaan ini sangat mengganggu saya, sampai saya mengerti penggunaan sederhana masing-masing.

Metode Kelas: Python tidak seperti Java dan C ++ tidak memiliki konstruktor yang berlebihan. Maka untuk mencapai ini Anda bisa menggunakan classmethod. Contoh berikut akan menjelaskan hal ini

Mari kita anggap kita memiliki Personkelas yang membutuhkan dua argumen first_namedan last_namedan menciptakan instance dari Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Sekarang, jika persyaratan datang di mana Anda perlu membuat kelas menggunakan satu nama saja, hanya a first_name, Anda tidak dapat melakukan sesuatu seperti ini dengan Python.

Ini akan memberi Anda kesalahan ketika Anda akan mencoba membuat objek (contoh).

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

Namun, Anda dapat mencapai hal yang sama menggunakan @classmethodseperti yang disebutkan di bawah ini

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, "")

Metode Statis: Ini agak sederhana, tidak terikat pada instance atau kelas dan Anda dapat memanggilnya menggunakan nama kelas.

Jadi katakanlah dalam contoh di atas Anda memerlukan validasi yang first_nametidak boleh melebihi 20 karakter, Anda cukup melakukan ini.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

dan Anda cukup menelepon menggunakan class name

Person.validate_name("Gaurang Shah")
Gaurang Shah
sumber
2
Ini adalah posting lama, tetapi cara yang lebih pythonic untuk mencapai konstruktor yang menerima salah satu atau dua argumen akan menggunakan def __init__(self, first_name, last_name="")bukan metode kelas get_person. Hasilnya juga akan persis sama dalam kasus ini.
akarilimano
31

Saya pikir pertanyaan yang lebih baik adalah "Kapan Anda menggunakan @classmethod vs @staticmethod?"

@classmethod memungkinkan Anda akses mudah ke anggota pribadi yang terkait dengan definisi kelas. ini adalah cara terbaik untuk melakukan lajang, atau kelas pabrik yang mengontrol jumlah instance dari objek yang dibuat yang ada.

@staticmethod memberikan keuntungan kinerja marjinal, tapi saya belum melihat penggunaan produktif metode statis dalam kelas yang tidak dapat dicapai sebagai fungsi mandiri di luar kelas.

Nathan Tregillus
sumber
31

@decorators ditambahkan dalam python 2.4 Jika Anda menggunakan python <2.4 Anda dapat menggunakan fungsi classmethod () dan staticmethod ().

Misalnya, jika Anda ingin membuat metode pabrik (Fungsi yang mengembalikan instance dari implementasi kelas yang berbeda tergantung pada argumen yang didapatnya), Anda dapat melakukan sesuatu seperti:

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)

Perhatikan juga bahwa ini adalah contoh yang baik untuk menggunakan metode class dan metode statis, Metode statis jelas milik kelas, karena menggunakan Cluster kelas secara internal. Metode class hanya membutuhkan informasi tentang kelas, dan tidak ada instance dari objek.

Manfaat lain dari membuat _is_cluster_formetode metodemetode adalah agar subkelas dapat memutuskan untuk mengubah implementasinya, mungkin karena cukup generik dan dapat menangani lebih dari satu jenis kluster, jadi hanya memeriksa nama kelas tidak akan cukup.

Jens Timmerman
sumber
28

Metode Statis:

  • Fungsi sederhana tanpa argumen sendiri.
  • Bekerja pada atribut kelas; bukan pada atribut instance.
  • Dapat dipanggil melalui kelas dan instance.
  • Fungsi built-in staticmethod () digunakan untuk membuatnya.

Manfaat Metode Statis:

  • Ini melokalkan nama fungsi di lingkup kelas
  • Itu memindahkan kode fungsi lebih dekat ke tempat itu digunakan
  • Lebih nyaman untuk mengimpor versus fungsi tingkat modul karena setiap metode tidak harus diimpor secara khusus

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

Metode Kelas:

  • Fungsi yang memiliki argumen pertama sebagai nama kelas.
  • Dapat dipanggil melalui kelas dan instance.
  • Ini dibuat dengan classmethod in-built function.

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

@staticmethodhanya menonaktifkan fungsi default sebagai deskriptor metode. classmethod membungkus fungsi Anda dalam sebuah wadah yang bisa dipanggil yang meneruskan referensi ke kelas pemilik sebagai argumen pertama:

>>> class C(object):
...  pass
... 
>>> def f():
...  pass
... 
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>

Sebenarnya, classmethodmemiliki overhead runtime tetapi memungkinkan untuk mengakses kelas pemilik. Atau saya sarankan menggunakan metaclass dan meletakkan metode kelas pada metaclass itu:

>>> class CMeta(type):
...  def foo(cls):
...   print cls
... 
>>> class C(object):
...  __metaclass__ = CMeta
... 
>>> C.foo()
<class '__main__.C'>
Armin Ronacher
sumber
1
Salah satu kemungkinan kelemahan dari sebuah metaclass untuk ini yang segera terjadi pada saya adalah bahwa Anda tidak dapat memanggil metode class secara langsung atas contoh. c = C(); c.foo()memunculkan AttributeError, yang harus Anda lakukan type(c).foo(). Ini mungkin juga dianggap sebagai fitur - saya tidak bisa memikirkan mengapa Anda ingin melakukannya.
Aaron Hall
20

Panduan pasti tentang cara menggunakan metode statis, kelas atau abstrak dengan Python adalah salah satu tautan yang bagus untuk topik ini, dan rangkum sebagai berikut.

@staticmethodfungsi tidak lebih dari fungsi yang didefinisikan di dalam kelas. Itu bisa dipanggil tanpa membuat instance kelas terlebih dahulu. Definisi itu tidak dapat diubah melalui pewarisan.

  • Python tidak harus instantiate metode terikat untuk objek.
  • Ini memudahkan pembacaan kode, dan tidak tergantung pada keadaan objek itu sendiri;

@classmethodfungsi juga dapat dipanggil tanpa membuat instance kelas, tetapi definisinya mengikuti Sub kelas, bukan kelas Induk, melalui pewarisan, dapat ditimpa oleh subclass. Itu karena argumen pertama untuk @classmethodfungsi harus selalu cls (class).

  • Metode pabrik , yang digunakan untuk membuat turunan untuk suatu kelas menggunakan misalnya beberapa jenis pra-pemrosesan.
  • Metode statis yang memanggil metode statis : jika Anda membagi metode statis dalam beberapa metode statis, Anda tidak boleh membuat hard-code nama kelas tetapi menggunakan metode kelas
zangw
sumber
Terima kasih @ zangw - ketidakmampuan bawaan fungsi statis adalah perbedaan utama yang tampak
hard_working_ant
18

Hanya argumen pertama yang berbeda :

  • metode normal: objek saat ini jika secara otomatis diteruskan sebagai argumen pertama (tambahan)
  • classmethod: kelas objek saat ini secara otomatis diteruskan sebagai argumen tinju (tambahan)
  • staticmethod: tidak ada argumen tambahan yang diteruskan secara otomatis. Apa yang Anda berikan ke fungsi adalah apa yang Anda dapatkan.

Lebih detail ...

metode normal

Ketika metode objek dipanggil, ia secara otomatis diberikan argumen tambahan selfsebagai argumen pertamanya. Yaitu, metode

def f(self, x, y)

harus dipanggil dengan 2 argumen. selfsecara otomatis berlalu, dan itu adalah objek itu sendiri .

metode kelas

Ketika metode tersebut didekorasi

@classmethod
def f(cls, x, y)

argumen yang disediakan secara otomatis bukan self , tetapi kelas self .

metode statis

Ketika metode tersebut didekorasi

@staticmethod
def f(x, y)

metode ini tidak diberikan argumen otomatis sama sekali. Hanya diberikan parameter yang dipanggil dengan.

penggunaan

  • classmethod sebagian besar digunakan untuk konstruktor alternatif.
  • staticmethodtidak menggunakan status objek. Ini bisa menjadi fungsi eksternal untuk kelas. Itu hanya dimasukkan ke dalam kelas untuk mengelompokkan fungsi-fungsi dengan fungsi yang serupa (misalnya, seperti Mathmetode statis kelas Java )
class Point
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def frompolar(cls, radius, angle):
        """The `cls` argument is the `Point` class itself"""
        return cls(radius * cos(angle), radius * sin(angle))

    @staticmethod
    def angle(x, y):
        """this could be outside the class, but we put it here 
just because we think it is logically related to the class."""
        return atan(y, x)


p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)

angle = Point.angle(3, 2)
blue_note
sumber
17

Biarkan saya memberitahu kesamaan antara metode yang dihiasi dengan @classmethod vs @staticmethod terlebih dahulu.

Kesamaan: Keduanya dapat dipanggil di Kelas itu sendiri, bukan hanya contoh kelas. Jadi, keduanya merupakan metode Kelas .

Perbedaan: Metode kelas akan menerima kelas itu sendiri sebagai argumen pertama, sedangkan metode statis tidak.

Jadi metode statis, dalam arti tertentu, tidak terikat pada Kelas itu sendiri dan hanya menggantung di sana hanya karena mungkin memiliki fungsi terkait.

>>> 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',)
Selva
sumber
11

Pertimbangan lain sehubungan dengan staticmethod vs classmethod muncul dengan warisan. Katakanlah Anda memiliki kelas berikut:

class Foo(object):
    @staticmethod
    def bar():
        return "In Foo"

Dan Anda kemudian ingin menimpa bar()di kelas anak:

class Foo2(Foo):
    @staticmethod
    def bar():
        return "In Foo2"

Ini berfungsi, tetapi perhatikan bahwa sekarang bar()implementasi di kelas anak ( Foo2) tidak dapat lagi memanfaatkan apa pun yang spesifik untuk kelas itu. Misalnya, katakan Foo2memiliki metode yang disebut magic()yang ingin Anda gunakan dalam Foo2implementasi 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" 

Solusi di sini akan memanggil Foo2.magic()di bar(), tapi kemudian Anda mengulangi diri sendiri (jika nama Foo2perubahan, Anda harus ingat untuk memperbarui bahwa bar()metode).

Bagi saya, ini adalah sedikit pelanggaran prinsip terbuka / tertutup , karena keputusan yang dibuat Fooberdampak pada kemampuan Anda untuk memperbaiki kode umum di kelas turunan (yaitu kurang terbuka untuk ekstensi). Jika bar()a classmethodkita akan baik-baik saja:

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

Memberi: In Foo2 MAGIC

Adam Parkin
sumber
7

Saya akan mencoba menjelaskan perbedaan dasar menggunakan contoh.

class A(object):
    x = 0

    def say_hi(self):
        pass

    @staticmethod
    def say_hi_static():
        pass

    @classmethod
    def say_hi_class(cls):
        pass

    def run_self(self):
        self.x += 1
        print self.x # outputs 1
        self.say_hi()
        self.say_hi_static()
        self.say_hi_class()

    @staticmethod
    def run_static():
        print A.x  # outputs 0
        # A.say_hi() #  wrong
        A.say_hi_static()
        A.say_hi_class()

    @classmethod
    def run_class(cls):
        print cls.x # outputs 0
        # cls.say_hi() #  wrong
        cls.say_hi_static()
        cls.say_hi_class()

1 - kita dapat langsung memanggil metode statis dan metode tanpa menginisialisasi

# A.run_self() #  wrong
A.run_static()
A.run_class()

2- Metode statis tidak dapat memanggil metode mandiri tetapi dapat memanggil metode statis dan metode lainnya

3 - Metode statis milik kelas dan tidak akan menggunakan objek sama sekali.

4- Metode kelas tidak terikat pada objek tetapi ke kelas.

Rizwan Mumtaz
sumber
iklan 2: Apakah Anda yakin? Bagaimana metode statis memanggil metode kelas? Ia tidak memiliki referensi untuk itu (ke kelasnya).
mirek
7

@classmethod: dapat digunakan untuk membuat akses global bersama ke semua instance yang dibuat dari kelas itu ..... seperti memperbarui catatan oleh banyak pengguna .... Saya khususnya menemukan itu menggunakan ful saat membuat lajang juga ..: )

@static method: tidak ada hubungannya dengan kelas atau instance yang dikaitkan dengan ... tetapi untuk keterbacaan dapat menggunakan metode statis

vijay
sumber
5

Anda mungkin ingin mempertimbangkan perbedaan antara:

Class A:
    def foo():  # no self parameter, no decorator
        pass

dan

Class B:
    @staticmethod
    def foo():  # no self parameter
        pass

Ini telah berubah antara python2 dan python3:

python2:

>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

python3:

>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()

Jadi menggunakan @staticmethodmetode yang hanya dipanggil langsung dari kelas telah menjadi opsional di python3. Jika Anda ingin memanggil mereka dari kelas dan instance, Anda masih perlu menggunakan @staticmethoddekorator.

Kasus-kasus lain telah dicakup oleh jawaban unutbus

David Schumann
sumber
5

Metode kelas menerima kelas sebagai argumen pertama implisit, seperti metode instance menerima instance. Ini adalah metode yang terikat ke kelas dan bukan objek dari kelas. Ini memiliki akses ke keadaan kelas karena mengambil parameter kelas yang menunjuk ke kelas dan bukan objek contoh. Itu dapat memodifikasi status kelas yang akan berlaku di semua instance kelas. Misalnya ia dapat memodifikasi variabel kelas yang akan berlaku untuk semua instance.

Di sisi lain, metode statis tidak menerima argumen pertama implisit, dibandingkan dengan metode kelas atau metode instance. Dan tidak dapat mengakses atau mengubah status kelas. Itu hanya milik kelas karena dari sudut pandang desain itu adalah cara yang benar. Tetapi dalam hal fungsi tidak terikat, saat runtime, ke kelas.

sebagai pedoman, gunakan metode statis sebagai utilitas, gunakan metode kelas misalnya sebagai pabrik. Atau mungkin untuk mendefinisikan singleton. Dan gunakan metode instan untuk memodelkan keadaan dan perilaku instance.

Semoga saya jelas!

Nicolae Petridean
sumber
4

Kontribusi saya menunjukkan perbedaan antara @classmethod,, @staticmethoddan metode instance, termasuk bagaimana sebuah instance dapat secara tidak langsung memanggil a @staticmethod. Tetapi alih-alih secara tidak langsung memanggil @staticmethoddari suatu contoh, menjadikannya pribadi mungkin lebih "pythonic." Mendapatkan sesuatu dari metode pribadi tidak ditunjukkan di sini tetapi pada dasarnya konsep yang sama.

#!python3

from os import system
system('cls')
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

class DemoClass(object):
    # instance methods need a class instance and
    # can access the instance through 'self'
    def instance_method_1(self):
        return 'called from inside the instance_method_1()'

    def instance_method_2(self):
        # an instance outside the class indirectly calls the static_method
        return self.static_method() + ' via instance_method_2()'

    # class methods don't need a class instance, they can't access the
    # instance (self) but they have access to the class itself via 'cls'
    @classmethod
    def class_method(cls):
        return 'called from inside the class_method()'

    # static methods don't have access to 'cls' or 'self', they work like
    # regular functions but belong to the class' namespace
    @staticmethod
    def static_method():
        return 'called from inside the static_method()'
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''

# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %   %

# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()

# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''

# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''

# call class_method()
print(democlassObj.class_method() + '\n')
'''  called from inside the class_method() '''

# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''

"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""
Michael Swartz
sumber
2

Metode kelas, seperti namanya, digunakan untuk membuat perubahan pada kelas dan bukan objek. Untuk membuat perubahan pada kelas, mereka akan memodifikasi atribut kelas (bukan atribut objek), karena itulah cara Anda memperbarui kelas. Ini adalah alasan bahwa metode kelas mengambil kelas (secara konvensional dilambangkan oleh 'cls') sebagai argumen pertama.

class A(object):
    m=54

    @classmethod
    def class_method(cls):
        print "m is %d" % cls.m

Metode statis di sisi lain, digunakan untuk melakukan fungsionalitas yang tidak terikat ke kelas yaitu mereka tidak akan membaca atau menulis variabel kelas. Oleh karena itu, metode statis tidak mengambil kelas sebagai argumen. Mereka digunakan sehingga kelas dapat melakukan fungsi yang tidak terkait langsung dengan tujuan kelas.

class X(object):
    m=54 #will not be referenced

    @staticmethod
    def static_method():
        print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Tushar Vazirani
sumber
2

Menganalisis @staticmethod secara harfiah memberikan wawasan yang berbeda.

Metode normal kelas adalah metode dinamis implisit yang menjadikan instance sebagai argumen pertama.
Sebaliknya, metode statis tidak mengambil contoh sebagai argumen pertama, sehingga disebut 'statis' .

Metode statis memang merupakan fungsi normal yang sama dengan yang di luar definisi kelas.
Untungnya dikelompokkan ke dalam kelas hanya untuk berdiri lebih dekat di mana ia diterapkan, atau Anda mungkin menggulirkan untuk menemukannya.

Kalkulus
sumber
2

Saya pikir memberikan versi Python murni staticmethoddan classmethodakan membantu untuk memahami perbedaan di antara mereka di tingkat bahasa.

Keduanya adalah deskriptor non-data (Akan lebih mudah untuk memahaminya jika Anda terbiasa dengan deskriptor terlebih dahulu).

class StaticMethod(object):
    "Emulate PyStaticMethod_Type() in Objects/funcobject.c"

    def __init__(self, f):
        self.f = f

    def __get__(self, obj, objtype=None):
        return self.f


class ClassMethod(object):
    "Emulate PyClassMethod_Type() in Objects/funcobject.c"
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, cls=None):
        def inner(*args, **kwargs):
            if cls is None:
                cls = type(obj)
            return self.f(cls, *args, **kwargs)
        return inner
Jacky1205
sumber
1

staticmethod tidak memiliki akses ke attibutes objek, kelas, atau kelas induk dalam hierarki warisan. Itu dapat dipanggil di kelas secara langsung (tanpa membuat objek).

classmethod tidak memiliki akses ke atribut objek. Namun itu dapat mengakses atribut dari kelas dan kelas induk di hierarki warisan. Itu dapat dipanggil di kelas secara langsung (tanpa membuat objek). Jika dipanggil pada objek maka itu sama dengan metode normal yang tidak mengakses self.<attribute(s)>dan mengakses self.__class__.<attribute(s)>saja.

Pikir kita memiliki kelas dengan b=2, kita akan membuat objek dan mengatur ulang ini b=4di dalamnya. Metode statis tidak dapat mengakses apa pun dari sebelumnya. Metodemetode .b==2hanya dapat mengakses , melalui cls.b. Metode normal dapat mengakses keduanya: .b==4via self.bdan .b==2via self.__class__.b.

Kita dapat mengikuti gaya KISS (tetap sederhana, bodoh): Jangan gunakan metode statis dan metode kelas, jangan gunakan kelas tanpa membuat instance mereka, hanya mengakses atribut objek self.attribute(s). Ada bahasa di mana OOP diimplementasikan seperti itu dan saya pikir itu bukan ide yang buruk. :)

mirek
sumber
Satu hal yang lebih penting untuk classmethods: Jika Anda memodifikasi atribut dalam metode kelas, semua objek yang ada dari kelas ini yang tidak secara eksplisit mengatur atribut ini akan memiliki nilai yang dimodifikasi.
mirek
-4

Peretasan cepat metode identik lainnya di iPython mengungkapkan bahwa @staticmethodmenghasilkan keuntungan kinerja marjinal (dalam nanodetik), tetapi sebaliknya tampaknya tidak berfungsi. Selain itu, setiap peningkatan kinerja mungkin akan terhapus oleh pekerjaan tambahan memproses metode melalui staticmethod()saat kompilasi (yang terjadi sebelum eksekusi kode ketika Anda menjalankan skrip).

Demi keterbacaan kode saya akan menghindari @staticmethodkecuali metode Anda akan digunakan untuk banyak pekerjaan, di mana nanodetik dihitung.

Cathal Garvey
sumber
7
"Kalau tidak, tampaknya tidak berfungsi": tidak sepenuhnya benar. Lihat diskusi di atas.
Keith Pinson