מודל הנתונים של פייתון הוא הבסיס למערכת האובייקטים של השפה. בפייתון, הכול הוא אובייקט – לא רק מבני נתונים,אלא גם פונקציות, מחלקות, ואפילו מודולים נחשבים כאובייקטים. כמו שכתוב בתיעוד הרשמי:
“כל המידע בתוכנית פייתון מיוצג על ידי אובייקטים או על ידי קשרים בין אובייקטים.”
אובייקטים
לכל אובייקט בפייתון יש שלוש תכונות עיקריות:
- זהות (Identity): מזהה ייחודי של האובייקט (ב-CPython זה בדרך כלל כתובת הזיכרון, וניתן לגשת אליה עם הפונקציה id()). האופרטור is משמש להשוואת זהות בין אובייקטים.
- טיפוס (Type): סוג האובייקט (כלומר המחלקה שלו), שקובע אילו פעולות ניתן לבצע עליו. גם הטיפוס עצמו הוא אובייקט (מהסוג type או תת-מחלקה שלו), והוא לא ניתן לשינוי. ניתן לבדוק את סוג האובייקט בעזרת הפונקציה type().
- ערך (Value): המידע או המצב הפנימי שהאובייקט מכיל. ישנם אובייקטים שהערך הפנימי שלהם יכול להשתנות (כמו רשימות ומילונים – mutable), ויש כאלה שלא ניתן לשנות לאחר יצירתם (כמו טאפלים, מחרוזות ומספרים – immutable).
הבנה של ההבדל בין אובייקטים ניתנים לשינוי (mutable) ואובייקטים שאינם ניתנים לשינוי (immutable) היא חשובה מאוד.
אובייקטים שאינם ניתנים לשינוי, כמו טאפלים או מחרוזות, לא ניתן לשנות לאחר יצירתם – כל פעולה שנראית כאילו היא משנה אותם בפועל יוצרת אובייקט חדש.
לעומת זאת, אובייקטים ניתנים לשינוי, כמו רשימות או מילונים, אפשר לשנות "במקום" – כלומר, ללא יצירה של אובייקט חדש.
ההבדל הזה חשוב לכתיבת קוד בטוח – למשל, שימוש בפרמטרים ברירת מחדל שהם אובייקטים ניתנים לשינוי בפונקציות עלול לגרום לבאגים שקשה לאתר.
זה גם משפיע על אופן ההעתקה של אובייקטים (העתקה שטחית לעומת העתקה עמוקה), ועל ההתנהגות שלהם כמפתחות במילונים או כפריטים בקבוצות – רק אובייקטים שאינם ניתנים לשינוי ניתנים לגיבוב (hashable) כברירת מחדל.
מתודות
מודל הנתונים של פייתון מגדיר מערך עשיר של מתודות מיוחדות (שנקראות לעיתים “dunder” – קיצור של double underscore, כמו __add__, __len__, __repr__), שמאפשרות למחלקות שאתם מגדירים להתנהג כמו טיפוסים מובנים בשפה.
באמצעות מימוש המתודות הללו, האובייקטים שלכם יכולים לתמוך בפעולות ובפרוטוקולים הבנויים של פייתון:
- הדמיית טיפוסים מספריים: מימוש מתודות כמו __add__, __sub__ וכו' מאפשר לקבוע כיצד יתבצעו פעולות חשבוניות על האובייקט.
- הדמיית מבני נתונים/רצפים: מימוש פרוטוקולים של מיכלים כמו __len__ (תמיכה ב-len()), __getitem__ (גישה לפי אינדקס ואיטרציה), __setitem__, __iter__, ו-__next__ כדי לתמוך באיטרציה.
- הצגת מחרוזות: __repr__ קובעת את המייצג הרשמי של האובייקט (שימושי לניפוי שגיאות), ו-__str__ משמשת להצגה ידידותית למשתמש.
- בקרה על גישה לשדות: __getattr__ ו-__setattr__ מאפשרות שליטה בהתנהגות כאשר ניגשים לשדה או מגדירים אותו. __getattribute__ היא מתודה מתקדמת יותר שנקראת תמיד בגישה לשדה, ודורשת זהירות בשימוש.
לדוגמה, אם תממשו את __iter__ ו-__next__ במחלקה – תוכלו להפוך את האובייקט שלכם לאיטרבילי (ולכן להשתמש בו בלולאת for). אם תממשו את __enter__ ו-__exit__, תוכלו להשתמש באובייקט עם הפקודה with (כמנהל הקשר – context manager, נושא שנדון בו בהמשך).
מודל הנתונים של פייתון הוא עוצמתי – הוא מאפשר לכם ליצור אובייקטים מותאמים אישית שמתנהגים כמו אובייקטים מובנים בשפה, וכך מאפשרים ביטוי טבעי של רעיונות. כך למשל, ספריות כמו Pandas או NumPy מצליחות להשתלב באופן חלק עם תחביר פייתון.
דוגמה – יצירת אובייקט איטרבילי מותאם אישית
הדוגמה הבאה מגדירה מיכל (container) מותאם אישית שמממש חלק מפרוטוקול הרצפים (sequence protocol), מה שמאפשר לבצע עליו איטרציה (מעבר בלולאה) ואינדוקס (גישה לפי אינדקס):
בקוד הזה, המחלקה FibSequence מסוגלת להפיק רצף פיבונאצ’י אינסופי. היא מממשת את __iter__ ו-__next__, ולכן היא מתנהגת כאיטרטור. בנוסף, היא מממשת גם את getitem, מה שמאפשר לגשת לאיברי רצף לפי אינדקס באמצעות סוגריים מרובעים. דוגמה זו מדגימה כיצד מודל הנתונים של פייתון מאפשר לשלב אובייקטים מותאמים אישית עם תחביר מובנה של אינרציה ואינדוקס.