Dengan Python, apa itu metaclasses dan untuk apa kita menggunakannya?
sumber
Dengan Python, apa itu metaclasses dan untuk apa kita menggunakannya?
Metaclass adalah kelas suatu kelas. Kelas mendefinisikan bagaimana instance kelas (yaitu suatu objek) berperilaku sementara metaclass mendefinisikan bagaimana kelas berperilaku. Kelas adalah turunan dari metaclass.
Sementara di Python Anda bisa menggunakan callable acak untuk metaclasses (seperti yang ditunjukkan Jerub ), pendekatan yang lebih baik adalah menjadikannya kelas aktual itu sendiri. type
adalah metaclass biasa dalam Python. type
itu sendiri adalah kelas, dan itu adalah tipenya sendiri. Anda tidak akan dapat membuat ulang sesuatu seperti type
murni di Python, tetapi Python sedikit curang. Untuk membuat metaclass Anda sendiri di Python Anda benar-benar hanya ingin subclass type
.
Metaclass paling umum digunakan sebagai pabrik kelas. Saat Anda membuat objek dengan memanggil kelas, Python membuat kelas baru (ketika mengeksekusi pernyataan 'kelas') dengan memanggil metaclass. Dikombinasikan dengan normal __init__
dan __new__
metode, metaclasses memungkinkan Anda untuk melakukan 'hal ekstra' saat membuat kelas, seperti mendaftarkan kelas baru dengan beberapa registri atau mengganti kelas dengan sesuatu yang lain sama sekali.
Ketika class
pernyataan dieksekusi, Python pertama mengeksekusi tubuh class
pernyataan sebagai blok kode normal. Namespace yang dihasilkan (dict) memiliki atribut dari calon kelas. Metaclass ditentukan dengan melihat baseclasses dari calon kelas (metaclasses diwariskan), pada __metaclass__
atribut calon kelas (jika ada) atau __metaclass__
variabel global. Metaclass kemudian dipanggil dengan nama, basis dan atribut dari kelas untuk instantiate.
Namun, metaclasses sebenarnya menentukan jenis kelas, bukan hanya pabrik untuk itu, sehingga Anda dapat melakukan lebih banyak dengan mereka. Anda dapat, misalnya, mendefinisikan metode normal pada metaclass. Metaclass-methods ini seperti metode-metode class dalam hal mereka dapat dipanggil di kelas tanpa instance, tetapi mereka juga tidak seperti metode-metode class dalam hal mereka tidak dapat dipanggil pada instance dari kelas. type.__subclasses__()
adalah contoh metode pada type
metaclass. Anda juga dapat menentukan metode 'sihir' normal, seperti __add__
, __iter__
dan __getattr__
, untuk mengimplementasikan atau mengubah cara kelas bertindak.
Berikut adalah contoh agregat dari potongan-potongan:
def make_hook(f):
"""Decorator to turn 'foo' method into '__foo__'"""
f.is_hook = 1
return f
class MyType(type):
def __new__(mcls, name, bases, attrs):
if name.startswith('None'):
return None
# Go over attributes and see if they should be renamed.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_hook', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Would register class %s now." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Alternatively, to autogenerate the classname as well as the class:
# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "Would unregister class %s now." % self
class MyObject:
__metaclass__ = MyType
class NoneSample(MyObject):
pass
# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)
class Example(MyObject):
def __init__(self, value):
self.value = value
@make_hook
def add(self, other):
return self.__class__(self.value + other.value)
# Will unregister the class
Example.unregister()
inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()
print inst + inst
class Sibling(MyObject):
pass
ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
__metaclass__
tidak didukung dalam Python 3. Dalam penggunaan Python 3class MyObject(metaclass=MyType)
, lihat python.org/dev/peps/pep-3115 dan jawabannya di bawah ini.Kelas sebagai objek
Sebelum memahami metaclasses, Anda harus menguasai kelas dengan Python. Dan Python memiliki ide yang sangat aneh tentang kelas apa, dipinjam dari bahasa Smalltalk.
Di sebagian besar bahasa, kelas hanyalah potongan kode yang menjelaskan cara membuat objek. Itu agak benar di Python juga:
Tetapi kelas lebih dari itu dalam Python. Kelas juga benda.
Ya, benda.
Segera setelah Anda menggunakan kata kunci
class
, Python menjalankannya dan membuat OBJECT. Intruksimembuat dalam memori objek dengan nama "ObjectCreator".
Objek ini (kelas) itu sendiri mampu membuat objek (instance), dan inilah mengapa ia kelas .
Tapi tetap saja, itu sebuah objek, dan karena itu:
misalnya:
Membuat kelas secara dinamis
Karena kelas adalah objek, Anda dapat membuatnya dengan cepat, seperti objek apa pun.
Pertama, Anda bisa membuat kelas dalam suatu fungsi menggunakan
class
:Tapi itu tidak begitu dinamis, karena Anda masih harus menulis seluruh kelas sendiri.
Karena kelas adalah objek, mereka harus dihasilkan oleh sesuatu.
Saat Anda menggunakan
class
kata kunci, Python membuat objek ini secara otomatis. Tetapi seperti kebanyakan hal dalam Python, ini memberi Anda cara untuk melakukannya secara manual.Ingat fungsinya
type
? Fungsi tua yang baik yang memungkinkan Anda mengetahui jenis objek adalah:Nah,
type
memiliki kemampuan yang sama sekali berbeda, ia juga dapat membuat kelas dengan cepat.type
dapat mengambil deskripsi kelas sebagai parameter, dan mengembalikan kelas.(Saya tahu, konyol bahwa fungsi yang sama dapat memiliki dua kegunaan yang sama sekali berbeda sesuai dengan parameter yang Anda berikan. Ini masalah karena kompatibilitas ke belakang di Python)
type
bekerja dengan cara ini:Dimana:
name
: nama kelasbases
: tuple dari kelas induk (untuk warisan, bisa kosong)attrs
: kamus yang berisi nama atribut dan nilaimisalnya:
dapat dibuat secara manual dengan cara ini:
Anda akan melihat bahwa kami menggunakan "MyShinyClass" sebagai nama kelas dan sebagai variabel untuk menyimpan referensi kelas. Mereka bisa berbeda, tetapi tidak ada alasan untuk mempersulit.
type
menerima kamus untuk mendefinisikan atribut kelas. Begitu:Dapat diterjemahkan ke:
Dan digunakan sebagai kelas normal:
Dan tentu saja, Anda dapat mewarisi darinya, jadi:
akan menjadi:
Akhirnya, Anda ingin menambahkan metode ke kelas Anda. Cukup tentukan fungsi dengan tanda tangan yang tepat dan tetapkan sebagai atribut.
Dan Anda dapat menambahkan lebih banyak metode setelah Anda secara dinamis membuat kelas, seperti menambahkan metode ke objek kelas yang dibuat secara normal.
Anda melihat tujuan kita: dengan Python, kelas adalah objek, dan Anda dapat membuat kelas dengan cepat, secara dinamis.
Inilah yang Python lakukan ketika Anda menggunakan kata kunci
class
, dan melakukannya dengan menggunakan metaclass.Apa itu metaclasses (akhirnya)
Metaclasses adalah 'barang' yang menciptakan kelas.
Anda mendefinisikan kelas untuk membuat objek, bukan?
Tapi kami belajar bahwa kelas Python adalah objek.
Nah, metaclasses adalah yang menciptakan objek-objek ini. Mereka adalah kelas kelas, Anda bisa membayangkannya dengan cara ini:
Anda telah melihat yang
type
memungkinkan Anda melakukan sesuatu seperti ini:Itu karena fungsinya
type
sebenarnya metaclass.type
adalah metaclass yang digunakan Python untuk membuat semua kelas di belakang layar.Sekarang Anda bertanya-tanya mengapa sih itu ditulis dalam huruf kecil, dan bukan
Type
?Yah, saya kira itu masalah konsistensi dengan
str
, kelas yang membuat objek string, danint
kelas yang membuat objek integer.type
hanya kelas yang menciptakan objek kelas.Anda melihatnya dengan memeriksa
__class__
atribut.Semuanya, dan maksud saya segalanya, adalah objek dengan Python. Itu termasuk int, string, fungsi dan kelas. Semuanya adalah benda. Dan semuanya telah dibuat dari sebuah kelas:
Sekarang, apa
__class__
salahnya__class__
?Jadi, metaclass hanyalah hal-hal yang menciptakan objek kelas.
Anda dapat menyebutnya 'pabrik kelas' jika diinginkan.
type
adalah metaclass built-in yang digunakan Python, tetapi tentu saja, Anda dapat membuat metaclass Anda sendiri.The
__metaclass__
atributDi Python 2, Anda bisa menambahkan
__metaclass__
atribut saat menulis kelas (lihat bagian selanjutnya untuk sintaks Python 3):Jika Anda melakukannya, Python akan menggunakan metaclass untuk membuat kelas
Foo
.Hati-hati, ini rumit.
Anda menulis
class Foo(object)
terlebih dahulu, tetapi objek kelasFoo
belum dibuat dalam memori.Python akan mencari
__metaclass__
dalam definisi kelas. Jika menemukannya, ia akan menggunakannya untuk membuat kelas objekFoo
. Jika tidak, itu akan digunakantype
untuk membuat kelas.Baca itu beberapa kali.
Saat kamu melakukan:
Python melakukan hal berikut:
Apakah ada
__metaclass__
atribut di dalamnyaFoo
?Jika ya, buat dalam objek memori kelas (saya katakan objek kelas, tetap dengan saya di sini), dengan nama
Foo
dengan menggunakan apa yang ada di__metaclass__
.Jika Python tidak dapat menemukan
__metaclass__
, ia akan mencari__metaclass__
pada tingkat MODUL, dan mencoba melakukan hal yang sama (tetapi hanya untuk kelas yang tidak mewarisi apa pun, pada dasarnya kelas gaya lama).Kemudian jika tidak dapat menemukan
__metaclass__
sama sekali, itu akan menggunakanBar
metaclass sendiri (orang tua pertama) (yang mungkin merupakan defaulttype
) untuk membuat objek kelas.Hati-hati di sini bahwa
__metaclass__
atribut tidak akan diwarisi, metaclass dari parent (Bar.__class__
) akan menjadi. JikaBar
menggunakan__metaclass__
atribut yang dibuatBar
dengantype()
(dan tidaktype.__new__()
), subclass tidak akan mewarisi perilaku itu.Sekarang pertanyaan besarnya adalah, apa yang bisa Anda masukkan
__metaclass__
?Jawabannya adalah: sesuatu yang bisa membuat kelas.
Dan apa yang bisa membuat kelas?
type
, atau apa pun yang membuat subclass atau menggunakannya.Metaclasses dalam Python 3
Sintaks untuk mengatur metaclass telah diubah dengan Python 3:
yaitu
__metaclass__
atribut tidak lagi digunakan, mendukung argumen kata kunci dalam daftar kelas dasar.Namun perilaku metaclasses sebagian besar tetap sama .
Satu hal yang ditambahkan ke metaclasses dalam python 3 adalah Anda juga dapat meneruskan atribut sebagai argumen-kata kunci ke dalam metaclass, seperti:
Baca bagian di bawah ini untuk cara python menangani ini.
Metaclasses khusus
Tujuan utama dari sebuah metaclass adalah untuk mengubah kelas secara otomatis, ketika itu dibuat.
Anda biasanya melakukan ini untuk API, di mana Anda ingin membuat kelas yang cocok dengan konteks saat ini.
Bayangkan sebuah contoh bodoh, di mana Anda memutuskan bahwa semua kelas dalam modul Anda harus memiliki atribut mereka ditulis dalam huruf besar. Ada beberapa cara untuk melakukan ini, tetapi satu cara adalah dengan mengatur
__metaclass__
di tingkat modul.Dengan cara ini, semua kelas modul ini akan dibuat menggunakan metaclass ini, dan kita hanya perlu memberi tahu metaclass untuk mengubah semua atribut menjadi huruf besar.
Untungnya,
__metaclass__
sebenarnya bisa berupa panggilan apa pun, tidak perlu menjadi kelas formal (saya tahu, sesuatu dengan 'kelas' dalam namanya tidak perlu menjadi kelas, pikir angka ... tapi ini membantu).Jadi kita akan mulai dengan contoh sederhana, dengan menggunakan fungsi.
Mari kita periksa:
Sekarang, mari kita lakukan hal yang persis sama, tetapi menggunakan kelas nyata untuk metaclass:
Mari kita menulis ulang di atas, tetapi dengan nama variabel yang lebih pendek dan lebih realistis sekarang kita tahu apa artinya:
Anda mungkin telah memperhatikan argumen tambahan
cls
. Tidak ada yang istimewa tentang itu:__new__
selalu menerima kelas yang didefinisikannya, sebagai parameter pertama. Sama seperti yang Anda milikiself
untuk metode biasa yang menerima instance sebagai parameter pertama, atau kelas yang menentukan untuk metode kelas.Tapi ini bukan OOP yang tepat. Kami menelepon
type
langsung dan kami tidak mengabaikan atau memanggil orang tua__new__
. Mari kita lakukan itu sebagai gantinya:Kita dapat membuatnya lebih bersih dengan menggunakan
super
, yang akan memudahkan pewarisan (karena ya, Anda dapat memiliki metaclasses, mewarisi dari metaclasses, mewarisi dari jenis):Oh, dan dengan python 3 jika Anda melakukan panggilan ini dengan argumen kata kunci, seperti ini:
Ini diterjemahkan ke dalam metaclass untuk menggunakannya:
Itu dia. Benar-benar tidak ada lagi tentang metaclasses.
Alasan di balik kerumitan kode menggunakan metaclasses bukan karena metaclasses, itu karena Anda biasanya menggunakan metaclasses untuk melakukan hal-hal memutar yang mengandalkan introspeksi, memanipulasi warisan, vars seperti
__dict__
, dll.Memang, metaclasses sangat berguna untuk melakukan ilmu hitam, dan karena itu hal-hal rumit. Tetapi dengan sendirinya, mereka sederhana:
Mengapa Anda menggunakan kelas metaclasses alih-alih fungsi?
Karena
__metaclass__
dapat menerima callable, mengapa Anda menggunakan kelas karena itu jelas lebih rumit?Ada beberapa alasan untuk melakukannya:
UpperAttrMetaclass(type)
, Anda tahu apa yang akan diikuti__new__
,__init__
dan__call__
. Yang akan memungkinkan Anda melakukan hal-hal yang berbeda. Bahkan jika biasanya Anda bisa melakukan semuanya__new__
, beberapa orang hanya lebih nyaman menggunakannya__init__
.Mengapa Anda menggunakan metaclasses?
Sekarang pertanyaan besar. Mengapa Anda menggunakan beberapa fitur rawan kesalahan yang tidak jelas?
Ya, biasanya Anda tidak:
Python Guru Tim Peters
Kasing penggunaan utama untuk metaclass adalah membuat API. Contoh khas dari ini adalah Django ORM. Ini memungkinkan Anda untuk mendefinisikan sesuatu seperti ini:
Tetapi jika Anda melakukan ini:
Itu tidak akan mengembalikan
IntegerField
objek. Ini akan mengembalikanint
, dan bahkan dapat mengambilnya langsung dari database.Ini dimungkinkan karena
models.Model
mendefinisikan__metaclass__
dan menggunakan beberapa sihir yang akan mengubahPerson
Anda yang baru saja didefinisikan dengan pernyataan sederhana menjadi kait kompleks ke bidang database.Django membuat sesuatu yang kompleks terlihat sederhana dengan mengekspos API sederhana dan menggunakan metaclasses, membuat ulang kode dari API ini untuk melakukan pekerjaan nyata di belakang layar.
Kata terakhir
Pertama, Anda tahu bahwa kelas adalah objek yang dapat membuat instance.
Sebenarnya, kelas itu sendiri adalah contoh. Dari metaclasses.
Semuanya adalah objek dengan Python, dan semuanya adalah instance dari kelas atau instance dari metaclasses.
Kecuali untuk
type
.type
sebenarnya metaclass sendiri. Ini bukan sesuatu yang Anda dapat mereproduksi dalam Python murni, dan dilakukan dengan menipu sedikit di tingkat implementasi.Kedua, metaclasses rumit. Anda mungkin tidak ingin menggunakannya untuk perubahan kelas yang sangat sederhana. Anda dapat mengubah kelas dengan menggunakan dua teknik berbeda:
99% dari waktu Anda membutuhkan perubahan kelas, Anda lebih baik menggunakan ini.
Tapi 98% dari waktu, Anda tidak perlu perubahan kelas sama sekali.
sumber
models.Model
tidak digunakan__metaclass__
tetapiclass Model(metaclass=ModelBase):
untuk referensiModelBase
kelas yang kemudian melakukan sihir metaclass tersebut. Pos yang bagus! Inilah sumber Django: github.com/django/django/blob/master/django/db/models/…__metaclass__
atribut tidak akan diwarisi, metaclass dari parent (Bar.__class__
) akan menjadi. JikaBar
menggunakan__metaclass__
atribut yang dibuatBar
dengantype()
(dan tidaktype.__new__()
), subclass tidak akan mewarisi perilaku itu. >> - Bisakah Anda / seseorang tolong jelaskan sedikit lebih dalam bagian ini?Now you wonder why the heck is it written in lowercase, and not Type?
- baik karena diimplementasikan dalam C - itu adalah alasan yang sama defaultdict adalah huruf kecil sedangkan OrderedDict (dalam python 2) adalah CamelCase normalCatatan, jawaban ini untuk Python 2.x seperti yang ditulis pada 2008, metaclasses sedikit berbeda dalam 3.x.
Metaclasses adalah saus rahasia yang membuat 'kelas' bekerja. Metaclass default untuk objek gaya baru disebut 'type'.
Metaclasses mengambil 3 args. ' nama ', ' pangkalan ' dan ' dict '
Di sinilah rahasianya dimulai. Cari dari mana nama, pangkalan dan dict berasal dari contoh definisi kelas ini.
Mari kita mendefinisikan metaclass yang akan menunjukkan bagaimana ' class: ' menyebutnya.
Dan sekarang, contoh yang benar-benar berarti sesuatu, ini akan secara otomatis membuat variabel dalam daftar "atribut" yang ditetapkan pada kelas, dan diatur ke Tidak ada.
Perhatikan bahwa perilaku ajaib yang
Initialised
mendapatkan dengan memiliki metaclassinit_attributes
tidak diteruskan ke subkelas dariInitialised
.Berikut ini adalah contoh yang lebih konkret, yang menunjukkan bagaimana Anda dapat mensubklasifikasikan 'tipe' untuk membuat metaclass yang melakukan tindakan saat kelas dibuat. Ini cukup rumit:
sumber
Yang lain telah menjelaskan bagaimana metaclasses bekerja dan bagaimana mereka masuk ke dalam sistem tipe Python. Berikut ini contoh untuk apa mereka dapat digunakan. Dalam kerangka pengujian yang saya tulis, saya ingin melacak urutan di mana kelas didefinisikan, sehingga saya nanti bisa instantiate mereka dalam urutan ini. Saya merasa paling mudah untuk melakukan ini menggunakan metaclass.
Apa pun yang merupakan subkelas
MyType
lalu mendapat atribut kelas_order
yang mencatat urutan di mana kelas didefinisikan.sumber
__init__(self)
kata siapatype(self)._order = MyBase.counter; MyBase.counter += 1
?Salah satu penggunaan metaclasses adalah menambahkan properti dan metode baru ke instance secara otomatis.
Misalnya, jika Anda melihat model Django , definisi mereka terlihat sedikit membingungkan. Tampaknya Anda hanya mendefinisikan properti kelas:
Namun, pada saat runtime objek Person diisi dengan segala macam metode yang berguna. Lihat sumber untuk beberapa metaclassery yang menakjubkan.
sumber
Saya pikir pengantar ONLamp untuk pemrograman metaclass ditulis dengan baik dan memberikan pengantar yang sangat baik untuk topik meskipun sudah beberapa tahun.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (diarsipkan di https://web.archive.org/web/20080206005253/http://www.onlamp. com / pub / a / python / 2003/04/17 / metaclasses.html )
Singkatnya: Kelas adalah cetak biru untuk pembuatan instance, metaclass adalah cetak biru untuk pembuatan kelas. Dapat dengan mudah dilihat bahwa dalam kelas Python perlu objek kelas satu juga untuk mengaktifkan perilaku ini.
Saya tidak pernah menulis sendiri, tapi saya pikir salah satu penggunaan terbaik dari metaclasses dapat dilihat dalam kerangka Django . Kelas model menggunakan pendekatan metaclass untuk memungkinkan gaya deklaratif menulis model baru atau kelas bentuk. Sementara metaclass menciptakan kelas, semua anggota mendapatkan kemungkinan untuk menyesuaikan kelas itu sendiri.
Hal yang tersisa untuk dikatakan adalah: Jika Anda tidak tahu apa itu metaclasses, probabilitas bahwa Anda tidak akan membutuhkannya adalah 99%.
sumber
TLDR: Sebuah metaclass instantiate dan mendefinisikan perilaku untuk kelas seperti halnya kelas instantiate dan mendefinisikan perilaku untuk sebuah instance.
Kodesemu:
Di atas seharusnya terlihat akrab. Nah, dari mana datangnya
Class
? Ini adalah instance dari metaclass (juga pseudocode):Dalam kode nyata, kita dapat melewatkan metaclass default
type
,, semua yang kita butuhkan untuk membuat instance sebuah kelas dan kita mendapatkan sebuah kelas:Secara berbeda
Kelas adalah instance sebagai metaclass adalah kelas.
Ketika kita instantiate objek, kita mendapatkan instance:
Demikian juga, ketika kita mendefinisikan kelas secara eksplisit dengan metaclass default
type
,, kita instantiate:Dengan kata lain, kelas adalah turunan dari metaclass:
Dengan kata lain, metaclass adalah kelas kelas.
Ketika Anda menulis definisi kelas dan Python mengeksekusi itu, ia menggunakan metaclass untuk instantiate objek kelas (yang, pada gilirannya, akan digunakan untuk instantiate instance dari kelas itu).
Seperti halnya kita dapat menggunakan definisi kelas untuk mengubah bagaimana instance objek kustom berperilaku, kita dapat menggunakan definisi kelas metaclass untuk mengubah cara objek kelas berperilaku.
Untuk apa mereka digunakan? Dari dokumen :
Namun demikian, biasanya disarankan bagi pengguna untuk menghindari penggunaan metaclasses kecuali benar-benar diperlukan.
Anda menggunakan metaclass setiap kali Anda membuat kelas:
Ketika Anda menulis definisi kelas, misalnya, seperti ini,
Anda instantiate objek kelas.
Ini sama dengan pemanggilan fungsional
type
dengan argumen yang sesuai dan menugaskan hasilnya ke variabel nama itu:Catatan, beberapa hal secara otomatis ditambahkan ke
__dict__
, yaitu, namespace:The metaclass dari objek yang kita buat, dalam kedua kasus, adalah
type
.(Catatan samping pada isi kelas
__dict__
:__module__
ada karena kelas harus tahu di mana mereka didefinisikan, dan__dict__
dan__weakref__
ada di sana karena kita tidak mendefinisikan__slots__
- jika kita mendefinisikan__slots__
kita akan menghemat sedikit ruang dalam instance, seperti kita dapat melarang__dict__
dan__weakref__
dengan mengecualikannya. Misalnya:... tapi saya ngelantur.)
Kita dapat memperluas
type
seperti definisi kelas lainnya:Inilah standar
__repr__
kelas:Salah satu hal paling berharga yang dapat kita lakukan secara default dalam menulis objek Python adalah menyediakannya dengan yang baik
__repr__
. Ketika kami menelepon,help(repr)
kami belajar bahwa ada tes yang bagus untuk__repr__
itu yang juga membutuhkan tes untuk kesetaraan -obj == eval(repr(obj))
. Implementasi sederhana berikut__repr__
dan__eq__
untuk instance kelas dari tipe kelas kami memberi kami demonstrasi yang dapat meningkatkan pada default__repr__
kelas:Jadi sekarang ketika kita membuat objek dengan metaclass ini,
__repr__
gema pada baris perintah memberikan tampilan yang jauh lebih jelek daripada default:Dengan
__repr__
definisi yang bagus untuk instance kelas, kami memiliki kemampuan yang lebih kuat untuk men-debug kode kami. Namun, pemeriksaan lebih lanjut denganeval(repr(Class))
tidak mungkin (karena fungsi agak tidak mungkin untuk eval dari standarnya__repr__
).Penggunaan yang diharapkan:
__prepare__
namespaceJika, misalnya, kita ingin tahu bagaimana urutan metode kelas dibuat, kita bisa memberikan perintah dict sebagai namespace kelas. Kami akan melakukan ini dengan
__prepare__
yang mengembalikan dict namespace untuk kelas jika diimplementasikan dalam Python 3 :Dan penggunaan:
Dan sekarang kita memiliki catatan urutan metode ini (dan atribut kelas lainnya) dibuat:
Catatan, contoh ini diadaptasi dari dokumentasi - enum baru di perpustakaan standar melakukan ini.
Jadi yang kami lakukan adalah membuat sebuah metaclass dengan membuat kelas. Kita juga bisa memperlakukan metaclass seperti halnya kelas lainnya. Ini memiliki urutan resolusi metode:
Dan memiliki kira-kira yang benar
repr
(yang kita tidak bisa lagi eval kecuali kita dapat menemukan cara untuk mewakili fungsi kita.):sumber
Pembaruan Python 3
Ada (pada titik ini) dua metode utama dalam sebuah metaclass:
__prepare__
, dan__new__
__prepare__
memungkinkan Anda menyediakan pemetaan khusus (seperti aOrderedDict
) untuk digunakan sebagai namespace saat kelas sedang dibuat. Anda harus mengembalikan instance namespace apa pun yang Anda pilih. Jika Anda tidak menerapkan__prepare__
normaldict
digunakan.__new__
bertanggung jawab atas pembuatan / modifikasi aktual dari kelas akhir.Metaclass tanpa tulang, tidak melakukan apa-apa ekstra akan suka:
Contoh sederhana:
Katakanlah Anda ingin kode validasi sederhana dijalankan pada atribut Anda - seperti itu harus selalu berupa a
int
atau astr
. Tanpa metaclass, kelas Anda akan terlihat seperti:Seperti yang Anda lihat, Anda harus mengulangi nama atribut dua kali. Hal ini memungkinkan kesalahan ketik bersama dengan bug menjengkelkan.
Metaclass sederhana dapat mengatasi masalah itu:
Inilah yang akan terlihat seperti metaclass (tidak digunakan
__prepare__
karena tidak diperlukan):Contoh menjalankan:
menghasilkan:
Catatan : Contoh ini cukup sederhana, bisa juga dilakukan dengan dekorator kelas, tetapi mungkin metaclass yang sebenarnya akan melakukan lebih banyak.
Kelas 'ValidateType' untuk referensi:
sumber
__set_name__(cls, name)
di deskriptor (ValidateType
) untuk mengatur nama di deskriptor (self.name
dan dalam kasus ini jugaself.attr
). Ini ditambahkan ke tidak harus menyelam ke dalam metaclasses untuk kasus penggunaan umum khusus ini (lihat PEP 487).Peran
__call__()
metode metaclass ' saat membuat instance kelasJika Anda telah melakukan pemrograman Python selama lebih dari beberapa bulan, Anda akhirnya akan menemukan kode yang terlihat seperti ini:
Yang terakhir dimungkinkan ketika Anda menerapkan
__call__()
metode ajaib di kelas.The
__call__()
Metode dipanggil saat sebuah instance dari kelas yang digunakan sebagai callable. Tetapi seperti yang telah kita lihat dari jawaban sebelumnya, kelas itu sendiri adalah turunan dari metaclass, jadi ketika kita menggunakan kelas sebagai callable (yaitu ketika kita membuat instance dari itu) kita sebenarnya memanggil__call__()
metode metaclassnya . Pada titik ini sebagian besar programmer Python agak bingung karena mereka telah diberitahu bahwa ketika membuat instance seperti ini,instance = SomeClass()
Anda memanggil__init__()
metodenya. Beberapa yang sudah digali sedikit lebih tahu bahwa sebelum__init__()
ada__new__()
. Nah, hari ini lapisan kebenaran lain sedang diungkapkan, sebelum__new__()
ada metaclass '__call__()
.Mari kita pelajari metode panggilan rantai dari perspektif khusus membuat instance kelas.
Ini adalah metaclass yang mencatat tepat saat sebelum instance dibuat dan saat itu akan mengembalikannya.
Ini adalah kelas yang menggunakan metaclass itu
Dan sekarang mari kita buat instance dari
Class_1
Perhatikan bahwa kode di atas tidak benar-benar melakukan apa pun selain mencatat tugas. Setiap metode mendelegasikan pekerjaan aktual untuk implementasi orang tuanya, sehingga menjaga perilaku default. Sejak
type
adalahMeta_1
's kelas induk (type
menjadi default orangtua metaclass) dan mempertimbangkan urutan urutan output di atas, kita sekarang memiliki petunjuk mengenai apa yang akan menjadi pelaksanaan semu daritype.__call__()
:Kita dapat melihat bahwa metode metaclass '
__call__()
adalah yang disebut pertama. Ini kemudian mendelegasikan pembuatan instance ke metode kelas__new__()
dan inisialisasi ke instance__init__()
. Itu juga yang akhirnya mengembalikan instance.Dari
__call__()
penjelasan di atas, tampaknya metaclass ' juga diberikan kesempatan untuk memutuskan apakah panggilan keClass_1.__new__()
atauClass_1.__init__()
pada akhirnya akan dilakukan. Selama pelaksanaannya itu benar-benar bisa mengembalikan objek yang belum tersentuh oleh salah satu dari metode ini. Ambil contoh pendekatan ini pada pola tunggal:Mari kita amati apa yang terjadi ketika berulang kali mencoba membuat objek tipe
Class_2
sumber
Metaclass adalah kelas yang memberi tahu bagaimana (beberapa) kelas lain harus dibuat.
Ini adalah kasus di mana saya melihat metaclass sebagai solusi untuk masalah saya: Saya punya masalah yang sangat rumit, yang mungkin bisa diselesaikan secara berbeda, tetapi saya memilih untuk menyelesaikannya menggunakan metaclass. Karena kerumitannya, ini adalah salah satu dari beberapa modul yang telah saya tulis di mana komentar dalam modul melampaui jumlah kode yang telah ditulis. Ini dia...
sumber
Versi tl; dr
The
type(obj)
Fungsi membuat Anda jenis objek.The
type()
kelas adalah yang metaclass .Untuk menggunakan metaclass:
type
adalah metaclass sendiri. Kelas kelas adalah metaclass - tubuh kelas adalah argumen yang diteruskan ke metaclass yang digunakan untuk membangun kelas.Di sini Anda dapat membaca tentang cara menggunakan metaclasses untuk menyesuaikan konstruksi kelas.
sumber
type
sebenarnyametaclass
- kelas yang menciptakan kelas lain. Sebagian besarmetaclass
adalah subclass daritype
. Themetaclass
menerimanew
kelas sebagai argumen pertama dan menyediakan akses ke objek kelas dengan perincian seperti yang disebutkan di bawah ini:Note:
Perhatikan bahwa kelas tidak dipakai setiap saat; tindakan sederhana menciptakan kelas memicu eksekusi
metaclass
.sumber
Kelas python adalah objek itu sendiri - sebagai contoh - dari meta-class mereka.
Metaclass default, yang diterapkan ketika ketika Anda menentukan kelas sebagai:
kelas meta digunakan untuk menerapkan beberapa aturan ke seluruh rangkaian kelas. Misalnya, anggap Anda sedang membangun ORM untuk mengakses database, dan Anda ingin catatan dari setiap tabel dari kelas yang dipetakan ke tabel itu (berdasarkan bidang, aturan bisnis, dll.,), Kemungkinan penggunaan metaclass adalah misalnya, koneksi pool logic, yang dibagikan oleh semua kelas catatan dari semua tabel. Penggunaan lain adalah logika untuk mendukung kunci asing, yang melibatkan beberapa kelas catatan.
ketika Anda mendefinisikan metaclass, Anda mengetikkan subclass, dan dapat mengganti metode ajaib berikut untuk memasukkan logika Anda.
Bagaimanapun, keduanya adalah kait yang paling umum digunakan. metaclassing sangat kuat, dan di atas tidak ada daftar dekat dan lengkap kegunaan untuk metaclassing.
sumber
Fungsi type () dapat mengembalikan tipe objek atau membuat tipe baru,
sebagai contoh, kita dapat membuat kelas Hi dengan fungsi type () dan tidak perlu menggunakan cara ini dengan kelas Hi (objek):
Selain menggunakan tipe () untuk membuat kelas secara dinamis, Anda bisa mengontrol perilaku pembuatan kelas dan menggunakan metaclass.
Menurut model objek Python, kelas adalah objek, sehingga kelas harus merupakan turunan dari kelas tertentu lainnya. Secara default, kelas Python adalah turunan dari kelas tipe. Yaitu, tipe adalah metaclass dari sebagian besar kelas bawaan dan metaclass dari kelas yang ditentukan pengguna.
Magic akan berlaku ketika kita memberikan argumen kata kunci dalam metaclass, ini menunjukkan interpreter Python untuk membuat CustomList melalui ListMetaclass. new (), pada titik ini, kita dapat memodifikasi definisi kelas, misalnya, dan menambahkan metode baru dan kemudian mengembalikan definisi yang direvisi.
sumber
Selain jawaban yang dipublikasikan saya dapat mengatakan bahwa a
metaclass
mendefinisikan perilaku untuk kelas. Jadi, Anda dapat secara eksplisit mengatur metaclass Anda. Setiap kali Python mendapatkan kata kunciclass
maka itu mulai mencarimetaclass
. Jika tidak ditemukan - tipe metaclass default digunakan untuk membuat objek kelas. Menggunakan__metaclass__
atribut, Anda dapat mengaturmetaclass
kelas Anda:Ini akan menghasilkan output seperti ini:
Dan, tentu saja, Anda dapat membuat Anda sendiri
metaclass
untuk mendefinisikan perilaku kelas apa pun yang dibuat menggunakan kelas Anda.Untuk melakukan itu,
metaclass
kelas tipe default Anda harus diwarisi karena ini adalah yang utamametaclass
:Outputnya adalah:
sumber
Dalam pemrograman berorientasi objek, metaclass adalah kelas yang instansnya adalah kelas. Sama seperti kelas biasa mendefinisikan perilaku objek tertentu, metaclass mendefinisikan perilaku kelas tertentu dan contohnya. Istilah metaclass berarti sesuatu yang digunakan untuk membuat kelas. Dengan kata lain, itu adalah kelas suatu kelas. Metaclass digunakan untuk membuat kelas jadi seperti objek menjadi turunan dari kelas, kelas adalah turunan dari metaclass. Dalam kelas python juga dianggap objek.
sumber
Berikut ini contoh lain dari apa yang dapat digunakan untuk:
metaclass
untuk mengubah fungsi instance-nya (kelas).Yang
metaclass
kuat, ada banyak hal (seperti sihir monyet) yang bisa Anda lakukan dengan itu, tapi hati-hati ini mungkin hanya diketahui oleh Anda.sumber
Kelas, dengan Python, adalah objek, dan seperti objek lainnya, itu adalah turunan dari "sesuatu". "Sesuatu" ini adalah apa yang disebut sebagai Metaclass. Metaclass ini adalah jenis kelas khusus yang menciptakan objek kelas lain. Karenanya, metaclass bertanggung jawab untuk membuat kelas baru. Ini memungkinkan programmer untuk menyesuaikan cara kelas dihasilkan.
Untuk membuat metaclass, biasanya dilakukan penimpa metode baru () dan init (). new () dapat diganti untuk mengubah cara objek dibuat, sedangkan init () dapat diganti untuk mengubah cara menginisialisasi objek. Metaclass dapat dibuat dengan beberapa cara. Salah satu caranya adalah menggunakan fungsi type (). fungsi type (), ketika dipanggil dengan 3 parameter, membuat metaclass. Parameternya adalah: -
Cara lain untuk membuat metaclass terdiri dari kata kunci 'metaclass'. Tentukan metaclass sebagai kelas sederhana. Dalam parameter kelas bawaan, lulus metaclass = metaclass_name
Metaclass dapat secara khusus digunakan dalam situasi berikut: -
sumber
Perhatikan bahwa pada python 3.6, metode dunder baru
__init_subclass__(cls, **kwargs)
diperkenalkan untuk menggantikan banyak kasus penggunaan umum untuk metaclasses. Apakah dipanggil ketika subclass dari kelas mendefinisikan dibuat. Lihat dokumen python .sumber
Metaclass adalah jenis kelas yang mendefinisikan bagaimana kelas akan berperilaku seperti atau kita dapat mengatakan bahwa kelas itu sendiri adalah turunan dari sebuah metaclass.
sumber