Definisi kelas dinamis dengan Python

Definisi objek dinamis dapat dipahami sebagai definisi pada saat runtime. Tidak seperti definisi statis, yang digunakan dalam definisi kata kunci yang sudah dikenal dari sebuah kelas class, definisi dinamis menggunakan kelas sebaris type.



Ketik metaclass



Kelas tipe sering digunakan untuk mendapatkan tipe suatu objek. Contohnya seperti ini:



h = "hello"
type(h)
<class 'str'>


Tetapi memiliki kegunaan lain. Itu dapat menginisialisasi tipe baru.Seperti yang Anda ketahui, semua yang ada di Python adalah objek. Oleh karena itu, semua definisi memiliki tipe, termasuk kelas dan objek. Misalnya:



class A:
    pass
type(A)
<class 'type'>


Mungkin tidak sepenuhnya jelas mengapa kelas diberi jenis kelas type, sebagai lawan dari contohnya:



a = A()
type(a)
<class '__main__.A'>


Objek adiberi kelas sebagai tipe. Beginilah cara interpreter memperlakukan objek sebagai instance kelas. Kelas itu sendiri memiliki tipe kelas typekarena mewarisi dari kelas dasar object:



A.__bases__
(<class 'object'>,)


Jenis kelas object:



type(object)
<class 'type'>


Kelas objectdiwarisi oleh semua kelas secara default, yaitu:



class A(object):
    pass


Sama dengan:



class A:
    pass


Kelas yang didefinisikan mewarisi kelas dasar sebagai tipe. Namun, ini tidak menjelaskan mengapa kelas dasar objectadalah tipe kelas type. Intinya adalah, ini typeadalah metaclass. Seperti yang telah Anda ketahui, semua kelas mewarisi dari kelas dasar object, yang merupakan tipe metaclass type. Oleh karena itu, semua kelas juga memiliki tipe ini, termasuk metaclass itu sendiri type:



type(type)
<class 'type'>


Ini adalah "titik akhir pengetikan" dengan Python. Rantai pewarisan tipe ditutup di kelas type. Metaclass typeberfungsi sebagai basis untuk semua kelas dengan Python. Mudah untuk memverifikasi ini:



builtins = [list, dict, tuple]
for obj in builtins:
    type(obj)
<class 'type'>
<class 'type'>
<class 'type'>


Kelas adalah tipe data abstrak, dan instance-nya memiliki referensi kelas sebagai tipenya.



Menginisialisasi tipe baru dengan kelas tipe



Saat memeriksa tipe, kelas typediinisialisasi dengan satu argumen:



type(object) -> type


Dengan melakukan itu, ia mengembalikan tipe objek. Namun, kelas mengimplementasikan metode inisialisasi yang berbeda dengan tiga argumen, yang mengembalikan tipe baru:



type(name, bases, dict) -> new type


Ketik parameter inisialisasi



  • name

    Sebuah string yang mendefinisikan nama kelas baru (tipe).
  • bases

    Tupel kelas dasar (kelas yang akan diwariskan oleh kelas baru).
  • dict

    Kamus dengan atribut kelas masa depan. Biasanya dengan string dalam kunci dan tipe nilai yang dapat dipanggil.


Definisi kelas dinamis



Kami menginisialisasi kelas tipe baru, menyediakan semua argumen yang diperlukan dan menyebutnya:



MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>


Anda dapat bekerja dengan kelas baru seperti biasa:



m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>


Selain itu, metode ini setara dengan definisi kelas biasa:



class MyClass:
    pass


Mendefinisikan atribut kelas secara dinamis



Ada gunanya dalam kelas kosong, jadi muncul pertanyaan: bagaimana menambahkan atribut dan metode?



Untuk menjawab pertanyaan ini, pertimbangkan kode inisialisasi awal:



MyClass = type(“MyClass”, (object, ), dict())


Biasanya, atribut ditambahkan ke kelas pada tahap inisialisasi sebagai argumen ketiga - kamus. Anda dapat menentukan nama dan nilai atribut dalam kamus. Misalnya, ini bisa berupa variabel:



MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'


Definisi metode dinamis



Objek yang dapat dipanggil juga bisa diteruskan ke kamus, misalnya, metode:



def foo(self):
    return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'


Metode ini memiliki satu kelemahan yang signifikan - kebutuhan untuk mendefinisikan metode secara statis (menurut saya dalam konteks tugas metaprogramming, ini dapat dianggap sebagai kelemahan). Selain itu, definisi metode dengan parameter di selfluar badan kelas terlihat aneh. Oleh karena itu, mari kembali ke inisialisasi dinamis dari kelas tanpa atribut:



MyClass = type(“MyClass”, (object, ), dict())


Setelah menginisialisasi kelas kosong, Anda dapat menambahkan metode ke dalamnya secara dinamis, yaitu tanpa definisi statis eksplisit:



code = compile('def foo(self): print(“bar”)', "<string>", "exec")


compileAdalah fungsi built-in yang mengkompilasi kode sumber menjadi suatu objek. Kode dapat dijalankan dengan fungsi exec()atau eval().



Kompilasi parameter fungsi



  • source

    Source code, dapat berupa link ke modul.
  • filename

    Nama file tempat objek akan dikompilasi.
  • mode

    Jika ditentukan "exec", fungsi akan mengkompilasi kode sumber menjadi modul.


Hasil dari pekerjaan compileini adalah objek kelas code:



type(code)
<class 'code'>


Objek codeperlu diubah menjadi metode. Karena metode adalah fungsi, kita mulai dengan mengubah codeobjek kelas menjadi objek kelas function. Untuk melakukan ini, impor modul types:



from types import FunctionType, MethodType


Saya akan mengimpornya MethodTypekarena saya akan membutuhkannya nanti untuk mengonversi fungsi ke metode kelas.



function = FunctionType(code.co_consts[0], globals(), “foo”)


Parameter metode inisialisasi FunctionType



  • code

    Objek kelas code. code.co_consts[0]Adalah panggilan ke deskriptor co_constskelas code, yang merupakan tupel dengan konstanta dalam kode objek. Bayangkan sebuah objek codesebagai modul dengan satu fungsi yang kita coba tambahkan sebagai metode kelas. 0Apakah indeksnya, karena itu adalah satu-satunya konstanta dalam modul.
  • globals()

    Kamus variabel global.
  • name

    Parameter opsional yang menentukan nama fungsi.


Hasilnya adalah sebuah fungsi:



function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>


Selanjutnya, Anda perlu menambahkan fungsi ini sebagai metode kelas MyClass:



MyClass.foo = MethodType(function, MyClass)


Ekspresi yang cukup sederhana yang menetapkan fungsi kita ke metode kelas MyClass.



m = MyClass()
m.foo()
bar


Peringatan



Dalam 99% kasus, Anda bisa bertahan dengan definisi kelas statis. Namun, konsep metaprogramming bagus dalam mengungkap internal Python. Kemungkinan besar, akan sulit bagi Anda untuk menemukan penerapan metode yang dijelaskan di sini, meskipun dalam praktik saya ada kasus seperti itu.



Sudahkah Anda bekerja dengan objek dinamis? Mungkin dalam bahasa lain?



Tautan






All Articles