0Pricing
Python Academy · درس

واصفات الخصائص: __get__ و__set__ و__delete__

نفّذ بروتوكول الواصف للتحكم في الوصول إلى السمات

واصفات الخصائص: __get__ و__set__ و__delete__ درس مجاني في Python Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Python Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Python Academy 4 دروس في المجموع.

ما الوصف؟

الواصف كائن يعرّف __get__ أو __set__ أو __delete__، ويُعيَّن بصفته سمةً في الصنف. تستدعي Python هذه الأساليب عند الوصول إلى السمة.

class Descriptor:
    def __get__(self, obj, objtype=None):
        print(f"__get__ called, obj={obj}")
        return 42

class MyClass:
    attr = Descriptor()

print(MyClass().attr)   # __get__ called ... 42

الواصفات البيانية وغير البيانية

تعرّف الواصفات البيانية __set__ أو __delete__، وتكون لها الأولوية على __dict__ الخاص بالمثيل. أما الواصفات غير البيانية، التي تملك __get__ فقط، فتكون لها أولوية أقل من __dict__.

class DataDesc:
    def __get__(self, obj, t): return "data"
    def __set__(self, obj, val): pass

class NonDataDesc:
    def __get__(self, obj, t): return "nondata"

class C:
    d = DataDesc()
    n = NonDataDesc()

c = C()
c.__dict__["n"] = "instance"  # shadows the non-data desc
c.__dict__["d"] = "instance"  # does NOT shadow data desc
print(c.n)   # instance
print(c.d)   # data

توقيع __get__

__get__(self, obj, objtype): تمثّل obj المثيل، أو تكون None عند الوصول إلى السمة عبر الصنف؛ وتمثّل objtype الصنف.

class Verbose:
    def __get__(self, obj, objtype=None):
        if obj is None:
            return self   # accessed on class
        return f"value for {obj!r}"

class Widget:
    label = Verbose()

print(Widget.label)        # <Verbose object>
print(Widget().label)      # value for <Widget object>

__set__ والتحقق من الصحة

طبّق __set__(self, obj, value) للتحقق من قيمة أو تحويلها قبل تخزينها.

class PositiveInt:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, t):
        return obj.__dict__.get(self.name)

    def __set__(self, obj, val):
        if not isinstance(val, int) or val <= 0:
            raise ValueError(f"{self.name} must be a positive int")
        obj.__dict__[self.name] = val

class Rect:
    width  = PositiveInt()
    height = PositiveInt()

r = Rect()
r.width = 10
# r.width = -1  # ValueError

__set_name__

تستدعي Python 3.6 فأحدث __set_name__(owner, name) على الواصف عند تعيينه إلى صنف، مما يمنحه إمكانية الوصول إلى اسم السمة الخاصة به.

class Typed:
    def __set_name__(self, owner, name):
        self.public  = name
        self.private = "_" + name

    def __get__(self, obj, t):
        return None if obj is None else getattr(obj, self.private, None)

    def __set__(self, obj, val):
        setattr(obj, self.private, val)

class User:
    name = Typed()

u = User()
u.name = "Alice"
print(u.name)     # Alice

__delete__ لإزالة السمات

عرّف __delete__(self, obj) لاعتراض del obj.attr.

class Protected:
    def __set_name__(self, owner, name): self.name = name
    def __get__(self, obj, t):
        return obj.__dict__.get(self.name)
    def __set__(self, obj, v): obj.__dict__[self.name] = v
    def __delete__(self, obj):
        raise AttributeError(f"Cannot delete {self.name}")

class Config:
    host = Protected()

c = Config(); c.host = "localhost"
# del c.host   # AttributeError

الدوال واصفات غير بيانية

تطبّق الدوال __get__ لإعادة أسلوب مرتبط عند الوصول إليها من خلال مثيل. هكذا تعمل آلية ربط الأساليب في Python.

def greet(self):
    return f"Hello from {self}"

class C: pass
C.greet = greet

c = C()
print(c.greet())    # Hello from <C object>

classmethod وstaticmethod واصفان

إن classmethod وstaticmethod صنفان مضمّنان من الواصفات، يغلّفان الدوال ويغيّران القيمة التي يعيدها __get__.

class MyClass:
    @classmethod
    def from_string(cls, s):
        return cls()

# classmethod.__get__ returns a bound method with cls
# staticmethod.__get__ returns the plain function

واصف السمة الكسول

أنشئ واصفًا يحسب قيمةً مرةً واحدةً ويخزّنها مؤقتًا لكل مثيل.

class LazyAttr:
    def __init__(self, func): self.func = func
    def __set_name__(self, owner, name): self.name = name
    def __get__(self, obj, t):
        if obj is None: return self
        val = self.func(obj)
        obj.__dict__[self.name] = val   # shadows descriptor
        return val

class Report:
    @LazyAttr
    def summary(self):
        print("computing...")
        return "done"

r = Report()
print(r.summary)   # computing... done
print(r.summary)   # done (cached)

property واصف

إن property نفسه واصف بياني مطبّق بلغة C. يساعدك فهم الواصفات على فهم طريقة عمل كل من @property و@classmethod و@staticmethod.

class Circle:
    def __init__(self, r): self._r = r

    @property
    def radius(self): return self._r

    @radius.setter
    def radius(self, v):
        if v < 0: raise ValueError
        self._r = v

# property is: property.__get__ = fget, property.__set__ = fset

ترتيب البحث عن الواصفات

ترتيب البحث عن سمات Python: 1) واصفات البيانات من النوع (MRO)، 2) قاموس __dict__ الخاص بالمثيل، 3) الواصفات غير المعتمدة على البيانات ومتغيرات الفئة.

# Pseudocode for obj.attr:
# 1. Check type(obj).__mro__ for a data descriptor
# 2. Check obj.__dict__
# 3. Check type(obj).__mro__ for non-data descriptor or class var
# 4. Raise AttributeError

تحقق سريع

ما نوع الواصف الذي تكون له الأولوية على قاموس __dict__ الخاص بالمثيل؟

مراجعة

تعترض الواصفات الوصول إلى السمات عبر __get__ و__set__ و__delete__. تحجب واصفات البيانات قاموس __dict__ الخاص بالمثيل، بينما لا تحجبه الواصفات غير المعتمدة على البيانات. استخدم __set_name__ لمعرفة اسم السمة. كل من property وclassmethod وstaticmethod واصفات.

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

هل درس «واصفات الخصائص: __get__ و__set__ و__delete__» مجاني؟

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

ماذا ستتعلم في «واصفات الخصائص: __get__ و__set__ و__delete__»؟

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

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

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

كم من الوقت يستغرق درس «واصفات الخصائص: __get__ و__set__ و__delete__»؟

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

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

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

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

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