0Pricing
Python Academy · Pelajaran

Menulis Metaclass Kustom

Gunakan __new__ dan __init__ dalam metaclass untuk menyesuaikan pembuatan class.

Menulis Metaclass Kustom adalah pelajaran Python Academy gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Python Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Python Academy mencakup 4 pelajaran total.

Mendeklarasikan Metakelas

Teruskan metaclass=MyMeta dalam definisi kelas untuk menggunakan metakelas khusus. Metakelas tersebut harus mewarisi dari type.

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

print(type(Foo))   # <class '__main__.MyMeta'>

Menimpa __new__

__new__(mcs, name, bases, namespace) membuat objek kelas. Gunakan metode ini untuk mengubah ruang nama sebelum kelas dibuat.

class UpperAttrMeta(type):
    def __new__(mcs, name, bases, namespace):
        upper = {k.upper(): v for k, v in namespace.items()
                 if not k.startswith("_")}
        return super().__new__(mcs, name, bases, {**namespace, **upper})

class Config(metaclass=UpperAttrMeta):
    debug = True

print(Config.DEBUG)  # True

Menimpa __init__

__init__(cls, name, bases, namespace) dijalankan setelah kelas dibuat. Gunakan metode ini untuk penyiapan setelah pembuatan, seperti pendaftaran.

_registry = {}

class AutoRegisterMeta(type):
    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if bases:   # skip the base class itself
            _registry[name] = cls

class Handler(metaclass=AutoRegisterMeta): pass
class JSONHandler(Handler): pass
class XMLHandler(Handler):  pass

print(_registry)  # {'JSONHandler': ..., 'XMLHandler': ...}

Menegakkan Kontrak Antarmuka

Bangkitkan TypeError dalam __new__ jika metode yang diwajibkan tidak ada dalam isi kelas.

class InterfaceMeta(type):
    REQUIRED = frozenset({"process", "validate"})

    def __new__(mcs, name, bases, ns):
        if bases and not mcs.REQUIRED.issubset(ns):
            missing = mcs.REQUIRED - ns.keys()
            raise TypeError(f"{name} must define: {missing}")
        return super().__new__(mcs, name, bases, ns)

class Worker(metaclass=InterfaceMeta):
    def process(self): ...
    def validate(self): ...

Singleton melalui Metakelas

Timpa __call__ pada metakelas untuk mencegat pembuatan instans dan mengembalikan instans yang sama setiap kali.

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *a, **kw):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*a, **kw)
        return cls._instances[cls]

class DB(metaclass=SingletonMeta):
    pass

print(DB() is DB())  # True

Menambahkan Metode Secara Dinamis

Sisipkan metode utilitas ke setiap kelas yang menggunakan metakelas tersebut tanpa mengubah setiap kelas secara individual.

class MethodInjectorMeta(type):
    def __new__(mcs, name, bases, ns):
        ns["describe"] = lambda self: f"I am {type(self).__name__}"
        return super().__new__(mcs, name, bases, ns)

class Widget(metaclass=MethodInjectorMeta):
    pass

print(Widget().describe())   # I am Widget

__prepare__ untuk Ruang Nama Terurut

Timpa __prepare__ untuk menyediakan pemetaan khusus sebagai ruang nama kelas, misalnya untuk mempertahankan urutan definisi atau mendeteksi duplikat.

class NoDuplicatesMeta(type):
    class _NS(dict):
        def __setitem__(self, k, v):
            if k in self and not k.startswith("_"):
                raise ValueError(f"Duplicate attribute: {k}")
            super().__setitem__(k, v)

    @classmethod
    def __prepare__(mcs, name, bases):
        return mcs._NS()

Penyelesaian Konflik Metakelas

Jika beberapa kelas dasar memiliki metakelas yang berbeda, Python mengharuskan metakelas yang paling spesifik digunakan secara eksplisit, atau akan memunculkan kesalahan konflik.

class MetaA(type): pass
class MetaB(type): pass

# class Bad(A, B): pass  # TypeError: metaclass conflict

class MetaAB(MetaA, MetaB): pass   # resolve manually

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
class Good(A, B, metaclass=MetaAB): pass

Metakelas vs __init_subclass__

Untuk pendaftaran subkelas dan kait sederhana, __init_subclass__ lebih sederhana dan tidak memerlukan metakelas.

class Base:
    _registry = []
    def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        Base._registry.append(cls)

class ChildA(Base): pass
class ChildB(Base): pass

print(Base._registry)  # [ChildA, ChildB]

Melacak Akses Atribut

Timpa __getattribute__ pada metakelas untuk mencegat akses atribut pada kelas, bukan pada instans.

class TraceMeta(type):
    def __getattribute__(cls, name):
        val = super().__getattribute__(name)
        if not name.startswith("_"):
            print(f"Accessing class attr: {name}")
        return val

class Config(metaclass=TraceMeta):
    HOST = "localhost"

_ = Config.HOST   # logs: Accessing class attr: HOST

Pola Pengumpulan Field ORM

ORM bergaya Django menggunakan metakelas untuk mengumpulkan deskriptor Field dari isi kelas ke dalam registri _meta.

class Field:
    def __init__(self, col): self.col = col

class ModelMeta(type):
    def __new__(mcs, name, bases, ns):
        fields = {k: v for k, v in ns.items() if isinstance(v, Field)}
        ns["_fields"] = fields
        return super().__new__(mcs, name, bases, ns)

class User(metaclass=ModelMeta):
    name = Field("name")
    email = Field("email")

print(User._fields.keys())  # dict_keys(['name', 'email'])

Pemeriksaan Singkat

Dalam metakelas khusus, metode apa yang dipanggil untuk membuat objek kelas itu sendiri, sebelum __init__?

Ringkasan

Metakelas khusus menimpa __new__/__init__ untuk mengendalikan pembuatan kelas: menegakkan kontrak, mendaftarkan subkelas secara otomatis, menyisipkan metode, atau mengumpulkan deskriptor field. Lebih baik gunakan __init_subclass__ untuk kebutuhan pendaftaran sederhana.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Menulis Metaclass Kustom” gratis?

Ya — teks lengkap “Menulis Metaclass Kustom” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Python Academy, upgrade ke CoddyKit PRO. Kursus Python Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Menulis Metaclass Kustom”?

Gunakan __new__ dan __init__ dalam metaclass untuk menyesuaikan pembuatan class. Kamu berlatih Python Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Python Academy?

Tidak diperlukan pengalaman sebelumnya. Python Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Menulis Metaclass Kustom” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Python Academy ini?

Ya. Setiap pelajaran Python Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Cara Class Python Dibuat
  2. Menulis Metaclass Kustom
  3. Descriptor: __get__, __set__, __delete__
  4. __slots__ dan Optimasi Memori
← Kembali ke Python Academy