Karena kelas adalah instance dari metaclass, maka tidak terduga bahwa "metode instance" pada metaclass akan berperilaku seperti metode class.
Namun, ya, ada perbedaan - dan beberapa di antaranya lebih dari sekadar semantik:
- Perbedaan yang paling penting adalah bahwa metode dalam metaclass tidak "terlihat" dari suatu kelas instance . Itu terjadi karena pencarian atribut dalam Python (dengan cara yang disederhanakan - deskriptor dapat diutamakan) mencari atribut dalam instance - jika tidak ada dalam instance, Python kemudian mencari di kelas instance itu, dan kemudian pencarian melanjutkan kacamata super kelas, tetapi tidak pada kelas kelas. Stdlib Python memanfaatkan fitur ini dalam
abc.ABCMeta.register
metode ini. Fitur itu dapat digunakan untuk selamanya, karena metode yang berhubungan dengan kelas itu sendiri bebas untuk digunakan kembali sebagai atribut instance tanpa konflik apa pun (tetapi metode masih akan konflik).
- Perbedaan lain, meskipun jelas, adalah bahwa metode yang dideklarasikan dalam metaclass dapat tersedia di beberapa kelas, jika tidak terkait - jika Anda memiliki hierarki kelas yang berbeda, tidak terkait sama sekali dalam apa yang mereka hadapi, tetapi ingin beberapa fungsi umum untuk semua kelas , Anda harus membuat kelas mixin, yang harus dimasukkan sebagai basis di kedua hierarki (katakanlah untuk menyertakan semua kelas dalam registri aplikasi). (NB. Mixin terkadang menjadi panggilan yang lebih baik daripada metaclass)
- Classmethod adalah objek "classmethod" khusus, sedangkan metode dalam metaclass adalah fungsi biasa.
Jadi, kebetulan bahwa mekanisme yang digunakan oleh metode metode adalah " protokol deskriptor ". Sementara fungsi normal menampilkan __get__
metode yang akan menyisipkan self
argumen ketika mereka diambil dari sebuah instance, dan membiarkan argumen itu kosong ketika diambil dari suatu kelas, sebuah classmethod
objek memiliki yang berbeda __get__
, yang akan menyisipkan kelas itu sendiri ("pemilik") sebagai parameter pertama dalam kedua situasi.
Ini tidak membuat perbedaan praktis sebagian besar waktu, tetapi jika Anda ingin akses ke metode sebagai fungsi, untuk tujuan menambahkan secara dinamis menambahkan dekorator ke dalamnya, atau yang lain, untuk metode dalam metaclass meta.method
mengambil fungsi, siap digunakan , sementara Anda harus menggunakan cls.my_classmethod.__func__
untuk mengambilnya dari suatu metoda (dan kemudian Anda harus membuat yang lainclassmethod
objek dan menetapkan kembali, jika Anda melakukan pembungkus).
Pada dasarnya, ini adalah 2 contoh:
class M1(type):
def clsmethod1(cls):
pass
class CLS1(metaclass=M1):
pass
def runtime_wrap(cls, method_name, wrapper):
mcls = type(cls)
setattr(mcls, method_name, wrapper(getatttr(mcls, method_name)))
def wrapper(classmethod):
def new_method(cls):
print("wrapper called")
return classmethod(cls)
return new_method
runtime_wrap(cls1, "clsmethod1", wrapper)
class CLS2:
@classmethod
def classmethod2(cls):
pass
def runtime_wrap2(cls, method_name, wrapper):
setattr(cls, method_name, classmethod(
wrapper(getatttr(cls, method_name).__func__)
)
)
runtime_wrap2(cls1, "clsmethod1", wrapper)
Dengan kata lain: terlepas dari perbedaan penting bahwa metode yang didefinisikan dalam metaclass terlihat dari instance dan aclassmethod
objek tidak, perbedaan lainnya, pada saat runtime akan tampak tidak jelas dan tidak berarti - tetapi itu terjadi karena bahasa tidak perlu pergi keluar dari jalannya dengan aturan khusus untuk metode kelas: Kedua cara menyatakan metode kelas adalah mungkin, sebagai konsekuensi dari desain bahasa - satu, karena fakta bahwa kelas itu sendiri merupakan objek, dan yang lain, sebagai kemungkinan di antara banyak, dari penggunaan protokol deskriptor yang memungkinkan seseorang untuk mengkhususkan akses atribut dalam sebuah instance dan dalam sebuah kelas:
The classmethod
builtin didefinisikan dalam kode asli, tapi bisa saja akan dikodekan dalam python murni dan akan bekerja dengan cara yang sama persis. Kelas 5 baris di bawah ini dapat digunakan sebagai classmethod
dekorator tanpa perbedaan runtime dengan repr` @classmethod" at all (though distinguishable through introspection such as calls to
isinstance built-in , and even
tentunya):
class myclassmethod:
def __init__(self, func):
self.__func__ = func
def __get__(self, instance, owner):
return lambda *args, **kw: self.__func__(owner, *args, **kw)
Dan, di luar metode, menarik untuk diingat bahwa atribut khusus seperti @property
pada metaclass akan berfungsi sebagai atribut kelas khusus, sama saja, tanpa perilaku yang mengejutkan sama sekali.