0Pricing
Python Academy · บทเรียน

การเขียนเมตาคลาสแบบกำหนดเอง

ใช้ __new__ และ __init__ ในเมตาคลาสเพื่อปรับแต่งการสร้างคลาส

การเขียนเมตาคลาสแบบกำหนดเอง เป็นบทเรียน Python Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Python Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

การประกาศเมตาคลาส

ส่ง metaclass=MyMeta ในคำจำกัดความคลาสเพื่อใช้เมตาคลาสแบบกำหนดเอง เมตาคลาสต้องสืบทอดจาก type

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

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

การเขียนทับ __new__

__new__(mcs, name, bases, namespace) สร้างอ็อบเจ็กต์คลาส ใช้เมธอดนี้เพื่อแก้ไขเนมสเปซก่อนสร้างคลาส

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

การเขียนทับ __init__

__init__(cls, name, bases, namespace) ทำงานหลังจากสร้างคลาสแล้ว ใช้สำหรับการตั้งค่าหลังการสร้าง เช่น การลงทะเบียน

_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': ...}

การบังคับใช้สัญญาของส่วนติดต่อ

ทำให้เกิด TypeError ใน __new__ หากเมธอดที่จำเป็นไม่มีอยู่ในเนื้อหาของคลาส

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): ...

ซิงเกิลตันผ่านเมตาคลาส

เขียนทับ __call__ บนเมตาคลาสเพื่อดักการสร้างอินสแตนซ์ และคืนค่าอินสแตนซ์เดิมทุกครั้ง

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

การเพิ่มเมธอดแบบไดนามิก

แทรกเมธอดอรรถประโยชน์ลงในทุกคลาสที่ใช้เมตาคลาส โดยไม่ต้องแก้ไขแต่ละคลาสแยกกัน

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__ สำหรับเนมสเปซที่มีลำดับ

เขียนทับ __prepare__ เพื่อจัดเตรียมการแมปแบบกำหนดเองเป็นเนมสเปซของคลาส เช่น เพื่อรักษาลำดับการประกาศหรือตรวจจับรายการซ้ำ

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

การแก้ไขข้อขัดแย้งของเมตาคลาส

เมื่อคลาสฐานหลายคลาสมีเมตาคลาสแตกต่างกัน Python กำหนดให้ใช้เมตาคลาสที่สืบทอดลงมามากที่สุดอย่างชัดเจน มิฉะนั้นจะเกิดข้อผิดพลาดจากข้อขัดแย้ง

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

เมตาคลาสเทียบกับ __init_subclass__

สำหรับการลงทะเบียนคลาสย่อยและจุดเชื่อมต่ออย่างง่าย __init_subclass__ ใช้ง่ายกว่าและไม่ต้องมีเมตาคลาส

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]

การติดตามการเข้าถึงแอตทริบิวต์

เขียนทับ __getattribute__ บนเมตาคลาสเพื่อดักการเข้าถึงแอตทริบิวต์บน คลาส ไม่ใช่บนอินสแตนซ์

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

รูปแบบการรวบรวมฟิลด์ของ ORM

ORM แบบ Django ใช้เมตาคลาสเพื่อรวบรวมตัวบอกลักษณะ Field จากเนื้อหาของคลาสเข้าสู่รีจิสทรี _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'])

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ในเมตาคลาสแบบกำหนดเอง เมธอดใดถูกเรียกเพื่อสร้างอ็อบเจ็กต์คลาสเองก่อน __init__

สรุปทบทวน

เมตาคลาสแบบกำหนดเองเขียนทับ __new__/__init__ เพื่อควบคุมการสร้างคลาส เช่น บังคับใช้สัญญา ลงทะเบียนคลาสย่อยโดยอัตโนมัติ แทรกเมธอด หรือรวบรวมตัวบอกลักษณะของฟิลด์ ให้เลือกใช้ __init_subclass__ เมื่อต้องการการลงทะเบียนอย่างง่าย

คำถามที่พบบ่อย

บทเรียน “การเขียนเมตาคลาสแบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนเมตาคลาสแบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Python Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Python Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนเมตาคลาสแบบกำหนดเอง”

ใช้ __new__ และ __init__ ในเมตาคลาสเพื่อปรับแต่งการสร้างคลาส คุณปฏิบัติ Python Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Python Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Python Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนเมตาคลาสแบบกำหนดเอง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Python Academy นี้ได้ไหม

ได้ บทเรียน Python Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การสร้างคลาสของ Python
  2. การเขียนเมตาคลาสแบบกำหนดเอง
  3. ตัวบอกคุณสมบัติ: __get__, __set__, __delete__
  4. __slots__ และการเพิ่มประสิทธิภาพหน่วยความจำ
← กลับไปที่ Python Academy