0Pricing
Python Academy · درس

كتابة أصناف وصفية مخصصة

استخدم __new__ و__init__ في الأصناف الوصفية لتخصيص إنشاء الفئات

كتابة أصناف وصفية مخصصة درس مجاني في Python Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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): ...

Singleton عبر صنف فوقي

تجاوز __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__ عند الحاجة إلى تسجيل بسيط.

الأسئلة الشائعة

هل درس «كتابة أصناف وصفية مخصصة» مجاني؟

نعم — نص درس «كتابة أصناف وصفية مخصصة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Python Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Python Academy 4 دروس في المجموع.

ماذا ستتعلم في «كتابة أصناف وصفية مخصصة»؟

استخدم __new__ و__init__ في الأصناف الوصفية لتخصيص إنشاء الفئات تتمرن على Python Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Python Academy؟

لا تُشترط خبرة سابقة. Python Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «كتابة أصناف وصفية مخصصة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Python Academy هذا؟

نعم. كل درس في Python Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. كيفية إنشاء فئات Python
  2. كتابة أصناف وصفية مخصصة
  3. واصفات الخصائص: __get__ و__set__ و__delete__
  4. __slots__ وتحسين الذاكرة
← العودة إلى Python Academy