לדלג לתוכן

אנקפסולציה בפייתון מוסברת

בואו נדבר בכנות. אם אתם מגיעים מרקע של ג'אווה או C++, כנראה חיפשתם בפייתון מילת מפתח כמו private ולא מצאתם. זה לא מקרי, וזה גם לא חוסר של השפה. פייתון פשוט מתייחסת לאנקפסולציה בצורה שונה לגמרי, ובשביל להשתמש בה נכון צריך להבין את הפילוסופיה שמאחוריה, לא רק את התחביר.

מה זה בעצם אנקפסולציה

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

בג'אווה ובשפות דומות אכיפה הזאת קשיחה - יש מילות מפתח כמו private ו-protected שהקומפיילר בודק ואוכף. בפייתון אין שום דבר כזה. במקום זה יש מוסכמות.

קו תחתון בודד - אנחנו לא נאכף, אבל תבינו את הרמז

כשמפתח פייתון כותב שם משתנה עם קו תחתון בודד בהתחלה, כמו _balance, זה סימן ברור לכל מי שקורא את הקוד - "זה מיועד לשימוש פנימי במחלקה, אל תיגעו בזה ישירות מבחוץ". פייתון לא חוסמת אתכם מלגשת ל-_balance מבחוץ, זה עדיין עובד לגמרי טכנית, אבל זו הפרה של הסכם ג'נטלמני.

class BankAccount:
    def __init__(self, balance):
        self._balance = balance

    def deposit(self, amount):
        self._balance += amount

טכנית account._balance עדיין נגיש מבחוץ. אבל כל מפתח פייתון מנוסה שרואה קו תחתון בהתחלה יודע לא לגעת בזה ישירות, ולהשתמש בפעולות שהמחלקה חושפת במפורש.

קו תחתון כפול - name mangling

יש רמה נוספת, קצת יותר תקיפה, עם שני קווים תחתונים בהתחלה כמו __balance. זה לא הופך את המשתנה ל-private באמת, אבל פייתון עושה לו משהו שנקרא name mangling - היא משנה את השם מאחורי הקלעים ל-_BankAccount__balance, כלומר מוסיפה את שם המחלקה כתחילית.

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance

account = BankAccount(100)
# account.__balance -> שגיאה
# account._BankAccount__balance -> עדיין עובד

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

שליטה אמיתית עם property

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

class BankAccount:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, value):
        if value < 0:
            raise ValueError("היתרה לא יכולה להיות שלילית")
        self._balance = value

עכשיו אפשר לכתוב account.balance = 200 כאילו זו גישה ישירה למשתנה, אבל בפועל רץ קוד ולידציה שבודק שהערך תקין. אם מישהו ינסה account.balance = -50, תיזרק שגיאה. זה בדיוק היתרון של property - הוא נותן ממשק פשוט ונקי כלפי חוץ, בזמן שמאחורי הקלעים אפשר לשלוט על כל גישה וכתיבה.

הפילוסופיה - כולנו מבוגרים אחראיים

פייתון מכנה את הגישה שלה "we're all consenting adults here" - כולנו מבוגרים אחראיים. הרעיון הוא שהשפה לא צריכה להגן עליכם מעצמכם באמצעות חסימות טכניות קשיחות. במקום זה, היא נותנת מוסכמות ברורות - קו תחתון אומר "אל תיגע", ומצפה שתכבדו את זה, בדיוק כמו שלא הייתם נכנסים למשרד של מישהו רק כי הדלת לא נעולה. זה מאפשר יותר גמישות למי שבאמת צריך לגשת למשהו פנימי במקרה חריג, בלי לפרוץ שום הגנה מלאכותית.

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

לסיכום

אנקפסולציה בפייתון היא לא עניין של אכיפה טכנית קשיחה, אלא של מוסכמות ברורות וכלים כמו property שנותנים שליטה אמיתית כשצריך. זה מצריך קצת יותר משמעת מהמתכנת, אבל בתמורה מקבלים גמישות שבשפות עם private נוקשה פשוט אין.

הצטרפו לקהילה בדיסקורד