การเขียนเมตาคลาสแบบกำหนดเอง
ใช้ __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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การสร้างคลาสของ Python
- การเขียนเมตาคลาสแบบกำหนดเอง
- ตัวบอกคุณสมบัติ: __get__, __set__, __delete__
- __slots__ และการเพิ่มประสิทธิภาพหน่วยความจำ