Menu

מחלקות ב-Python: __init__, self, מתודות, ירושה ו-Dataclasses

מבוא למתחילים למחלקות ב-Python: איך מגדירים אותן, יוצרים מופעים, המתודה __init__, ירושה, ומתי עדיף להשתמש ב-dataclasses.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מחלקה עוטפת נתונים ואת מה שעושים איתם

מחלקה מאפשרת להגדיר סוג של דבר (משתמש, חשבון בנק, הגדרות, בקשה) ואת כל מה שהדבר הזה אמור לדעת לעשות. ה_דבר_ עצמו הוא מופע (instance) של המחלקה. ה_פעולות_ נקראות מתודות.

הנה המחלקה הפשוטה ביותר שאפשר:

נפרק את זה:

  • class User: פותחת הגדרת מחלקה. לפי המוסכמה, שמות מחלקות נכתבים ב-PascalCase.
  • __init__ היא ה_מאתחל_ (initializer). היא רצה אוטומטית כשיוצרים מופע חדש, ומגדירה את התכונות שלו בעזרת self.whatever = ....
  • greet היא מתודה. self הוא הדרך של מתודה להתייחס למופע שעליו היא נקראה.
  • User("Rosa", "rosa@example.com") יוצרת מופע. Python קוראת ל-__init__ כש-self מוגדר כמופע החדש ושאר הארגומנטים מועברים הלאה.
  • user.greet() קוראת למתודה. Python מעבירה את user בתור self אוטומטית.

self הוא פשוט הארגומנט הראשון

self הוא לא מילה שמורה. זה השם שנותנים לפרמטר הראשון של מתודות מופע: המופע שעליו המתודה רצה. אפשר לקרוא לו בכל שם; כולם משתמשים ב-self כי כך כולם עושים.

כל מתודת מופע מקבלת את self כפרמטר הראשון שלה. כך מתודה יודעת מאיזה מופע לקרוא או איזה מופע לשנות.

הוספת התנהגות

מתודות יכולות לקרוא מצב ולשנות אותו:

__repr__ היא עוד מתודה מיוחדת: וו (hook) של "איך להציג את האובייקט הזה כשמדפיסים אותו?". הגדרה טובה שלה משתלמת מאוד בזמן דיבוג.

תכונות מחלקה מול תכונות מופע

תכונות שמוגדרות בגוף המחלקה (מחוץ ל-__init__) משותפות לכל המופעים. תכונות שמוצבות על self הן של כל מופע בנפרד:

השתמשו בתכונות מחלקה לערכים שבאמת משותפים: קבועים, מונים, הגדרות. נתונים שייחודיים לכל מופע שימו ב-__init__.

ירושה

מחלקה יכולה לרשת ממחלקה אחרת, לקבל את המתודות והתכונות שלה בחינם, ולהוסיף או לדרוס לפי הצורך:

super() נותנת גישה למחלקת האב. משתמשים בה בדרך כלל בתוך __init__ כדי לאתחל את התכונות של האב לפני שמוסיפים את שלכם:

ירושה שימושית, אבל קל להגזים בה. מתכנתי Python חדשים פונים הרבה פעמים לירושה כשהרכבה (composition), כלומר החזקת אובייקט כתכונה, הייתה ברורה יותר. התחילו ממבנה מחלקות שטוח, והכניסו ירושה רק כשיש יחס "is-a" (הוא סוג של) אמיתי.

העמוד על ירושה ב-Python הולך רחוק יותר: הרחבת מתודה של האב עם super(), isinstance ו-issubclass, מחלקות בסיס מופשטות, ירושה מרובה וסדר רזולוציית המתודות.

Dataclasses: ברירת מחדל נוחה יותר לרשומות

כשרוצים בעיקר מחלקה שמחזיקה כמה תכונות, עם השוואה והצגה בסיסיות, @dataclass חוסך הרבה קוד תבניתי:

השוו את זה לכתיבה ידנית של __init__, __repr__ ו-__eq__. Dataclasses הן הבחירה הראשונה הנכונה כשאחרת הייתם כותבים class Thing שאין בה כלום מלבד __init__ שמגדיר תכונות.

Properties: תכונות מחושבות

לפעמים רוצים "תכונה" שבעצם מחושבת. @property גורם לזה להיראות כמו גישה לתכונה:

השתמשו ב-properties במקומות שבהם הייתם כותבים obj.get_something(): החלפה ב-property גורמת ל-API להרגיש טבעי יותר. אל תשתמשו בהן כדי להסתיר פעולות יקרות; משתמשים מצפים שגישה לתכונה תהיה מהירה.

פרטי לפי מוסכמה

ל-Python אין פרטיות אמיתית. לפי המוסכמה, תכונות שמתחילות בקו תחתון מיועדות להיות פרטיות: מצפים מהקוראים לא לגעת בהן מחוץ למחלקה:

class Cache:
    def __init__(self):
        self._store = {}

    def put(self, key, value):
        self._store[key] = value

    def get(self, key, default=None):
        return self._store.get(key, default)

שמות עם שני קווים תחתונים (__name) מפעילים name mangling, שמוסיף את שם המחלקה לשם התכונה. זה כלי למניעת התנגשויות בירושה, לא אמצעי אבטחה.

מתי לא להשתמש במחלקה

אם כל מה שיש לכם הוא אוסף של נתונים קשורים, dataclass, dict פשוט או named tuple יספיקו. אם כל מה שיש לכם הוא פונקציה אחת, היא לא צריכה מחלקה שתחזיק אותה. פנו למחלקות כשיש לכם גם מצב וגם התנהגות שקשורים זה לזה, במיוחד כשיהיו לכם מופעים רבים מאותו סוג.

דוגמה אחרונה

מאגר לוג קטן שמתנהג כמו קובץ:

שימו לב ל-__len__, עוד מתודת קסם. ההגדרה שלה גורמת ל-len(logger) לעבוד, כי len(x) הוא בעצם קריאה ל-x.__len__(). ל-Python יש עשרות ווים כאלה; תכירו אותם כשתצטרכו.

שאלות נפוצות

מה זו מחלקה ב-Python?

מחלקה (class) היא תבנית ליצירת אובייקטים שמאגדים יחד נתונים (תכונות) והתנהגות (מתודות). class User: מגדירה מחלקה בשם User; User(...) יוצרת מופע. מחלקות הן הכלי המרכזי של Python למידול דברים שיש להם גם מצב וגם התנהגות.

מה המשמעות של self ב-Python?

self הוא הפרמטר הראשון של כל מתודת מופע. כשקוראים ל-user.greet(), Python מעבירה את המופע user בתור self. בתוך המתודה, self.name ניגש לתכונת השם של אותו משתמש. השם self הוא מוסכמה, לא מילה שמורה, אבל כל מתכנתי Python משתמשים בו.

האם Python היא שפה מונחית עצמים?

כן: הכל ב-Python הוא אובייקט, כולל מספרים, מחרוזות ופונקציות. אפשר לכתוב תכנות מונחה עצמים כשזה מתאים, אבל Python לא מכריחה. תוכניות Python רבות בעולם האמיתי משתמשות במחלקות במשורה, ומעדיפות פונקציות ומבני נתונים פשוטים כשאפשר.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל