واصفات الخصائص: __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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- كيفية إنشاء فئات Python
- كتابة أصناف وصفية مخصصة
- واصفات الخصائص: __get__ و__set__ و__delete__
- __slots__ وتحسين الذاكرة