מטא-תכנות
מתייחס לטכניקות שבהן קוד מתייחס לקוד כאל נתונים – כלומר, קוד פייתון שמסוגל לשנות או לנתח קוד פייתון אחר (כמו שינוי מחלקות, פונקציות ועוד בזמן ריצה).
האופי הדינמי של פייתון מאפשר גמישות רבה במטא-תכנות – מה שיכול להוביל לתבניות עוצמתיות, אך דורש זהירות לשמירה על קריאות ותחזוקה של הקוד.
Descriptors
Descriptors הם תכונה מתקדמת מאוד בפייתון, שמאפשרת שליטה בגישה לשדות במחלקות.
כל אובייקט שמממש אחת מהמתודות: __get__, __set__, או __delete__ נחשב ל־descriptor.
כאשר משתמשים בו כשדה במחלקה – פייתון מפעילה את המתודות האלו כשניגשים לשדה, משנים אותו או מוחקים אותו.
המנגנון הזה עומד מאחורי:
- פונקציות שמתנהגות כמתודות באובייקטים
- @property
- ORMs כמו Django ו-SQLAlchemy (שדות בטבלה הם descriptors)
למשל, כאשר ניגשים ל־obj.attr, אם attr הוא descriptor, פייתון תקרא:
python
attr.__get__(obj, type(obj))
כנ"ל, השמה (obj.attr = value) תפעיל __set__.
שימושים נפוצים
- תכונות מחושבות (@property) – למעשה descriptor שמאפשר גם getter, setter, ו־deleter.
- בדיקת טיפוסים / ערכים – לדוגמה, לוודא ששדה הוא תמיד מספר חיובי.
- טעינה עצלה (Lazy Loading) – קריאה ראשונה מבצעת חישוב או שאילתת DB, והשאר משתמשים בערך המזומן.
- ORMs – בדנגו/SQLAlchemy, כל שדה כמו models.CharField הוא descriptor שמפעיל לוגיקה לגישה למסד.
דוגמה – Descriptor שמבצע בדיקת טיפוס
במקרה הזה, TypedAttribute מוודא שכל ערך שמוקצה לשדה הוא מהטיפוס המצופה.
השדות name ו־age במחלקה Person הם descriptors – כל השמה כמו self.name = name תפעיל את __set__.
הערכים נשמרים בפועל בתוך __dict__ של המופע.
המתודה __get__ מחזירה את הערך מ־__dict__, ו־__delete__ מונעת מחיקה של השדה.
פונקציות שמוגדרות בתוך מחלקות הן בעצמן descriptors – המתודה __get__ מחזירה מתודה "קשורה" לאובייקט (bound method) וזהו המנגנון שמאפשר שימוש ב־instance.method().