Menu
Coddy logo textTech

תכונות פרטיות

חלק מהיחידה תכנות מונחה עצמים במסלול ה-Python של Coddy. שיעור 15 מתוך 64.

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

למה אנחנו צריכים תכונות פרטיות? דמיינו שיש לכם חשבון בנק. אתם לא רוצים שאף אחד ישנה את היתרה שלכם ישירות — עליו לפעול בערוצים המתאימים (שיטות הפקדה/משיכה), כדי שהבנק יוכל לאמת את העסקה, לבדוק אם יש הונאה, לשמור רישומים ועוד. תכונות פרטיות פועלות באותו אופן — הן מגינות על הנתונים שלכם מפני שינויים שגויים.

הנה דוגמה לשימוש בקו תחתון יחיד (מוסכמה ל״שימוש פנימי״):

class Person:
    def __init__(self, name, age):
        self._name = name    # "protected" - לשימוש פנימי
        self._age = age      # "protected" - לשימוש פנימי

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

person = Person("Alice", 30)
print(person._name)  # עובד, אבל לא מומלץ

השתמשו בקווים תחתונים כפולים לפרטיות חזקה יותר (עיוות שמות):

class Person:
    def __init__(self, name, age):
        self.__name = name   # "private" - השם שלו עובר שינוי (name mangling)
        self.__age = age     # "private" - השם שלו עובר שינוי (name mangling)
    
    def get_name(self):
        return self.__name
    
    def set_age(self, age):
        if age >= 0:
            self.__age = age
        else:
            print("Age must be positive!")

למה להשתמש בשיטות getter ו-setter? הן כמו שומרי סף. במקום לאפשר לכל אחד לשנות ישירות את __age (דבר שעלול לגרום לגילים שליליים או לנתונים לא תקינים אחרים), אנחנו מכריחים אותם להשתמש ב-set_age(), שמאמת את הקלט תחילה. כך נמנעים מבאגים ומבטיחים את שלמות הנתונים.

ל״קוד הנוסף״ יש מטרה:

  • get_name() - גישה מבוקרת לקריאת השם
  • set_age(age) - מאמת את הגיל לפני שמאפשר שינויים (בלי גילים שליליים!)
  • בלי השיטות האלה, מישהו עלול להגדיר age = -100 ולשבור את התוכנית שלך

השתמשו בשיטות הגישה כדי ליצור אינטראקציה עם מאפיינים פרטיים:

person = Person("Bob", 25)
print(person.get_name())  # Bob

person.set_age(30)        # תקין: הגיל הופך ל-30
person.set_age(-5)        # לא תקין: הגיל חייב להיות חיובי! (הגיל נשאר 30)

רואים את היתרון? השיטה set_age() מונעת נתונים לא תקינים. בלעדיה, הייתם עלולים ליצור בטעות אדם שגילו ‎-5, וזה לא הגיוני!

לתכונות עם שני קווים תחתונים מתבצע „עיוות שם”, אך עדיין אפשר לגשת אליהן:

person = Person("Charlie", 35)
# זה לא עובד:
# print(person.__name)  # AttributeError

# אבל זה עובד (לא מומלץ):
print(person._Person__name)  # Charlie

דוגמה מהעולם האמיתי — למה כל הקוד הזה?

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # פרטי: אי אפשר לשנות ישירות
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

# ללא מאפיינים פרטיים:
# account.__balance = -1000000  # אסון! יתרה שלילית מותרת

# עם מאפיינים פרטיים:
account = BankAccount(100)
account.deposit(50)           # בטוח: עבר אימות
print(account.get_balance())  # 150

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

פלט:

Alice
Bob
Age must be positive!
Charlie
150

קו תחתון יחיד _attribute פירושו, לפי המוסכמה, ״לשימוש פנימי בלבד״. שני קווים תחתונים __attribute מפעילים שינוי שמות כדי לספק פרטיות חזקה יותר. השתמשו במתודות getter/setter (מתודות גישה) כדי לעבוד כראוי עם נתונים פרטיים ולהוסיף אימות. ״הקוד הנוסף״ מונע באגים בכך שהוא מבטיח שהנתונים תמיד תקינים.

challenge icon

אתגר

קל

השלם את המחלקה User בקובץ user.py עם פונקציונליות של סיסמה פרטית, ולאחר מכן השתמש בה בקובץ driver.py. פעל לפי הערות ה־TODO כדי לממש את השיטות לבדיקה ולשינוי של הסיסמה.

נסו בעצמכם

# TODO: ייבאו את המחלקה User מהמודול user

# TODO: קבלו מקרה בדיקה מהקלט
test_case = input()

if test_case == "constructor_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    print("User created successfully")
    print("Initial password set")

elif test_case == "correct_password_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    # TODO: בדקו את המתודה check_password עם הסיסמה הנכונה
    #       הדפיסו "Password check successful!" אם היא מחזירה True
    #       הדפיסו "Password check failed!" אם היא מחזירה False
    result = user.check_password("secure123")
    if result:
        print("Password check successful!")
    else:
        print("Password check failed!")

elif test_case == "incorrect_password_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    # TODO: בדקו את המתודה check_password עם הסיסמה השגויה "wrong"
    #       הדפיסו "Incorrect password rejected!" אם היא מחזירה False
    #       הדפיסו "Security issue: incorrect password accepted!" אם היא מחזירה True
    result = user.check_password("wrong")
    if not result:
        print("Incorrect password rejected!")
    else:
        print("Security issue: incorrect password accepted!")

elif test_case == "change_password_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    # TODO: בדקו שינוי סיסמה מ-"secure123" ל-"newpass456"
    #       הדפיסו "Password changed successfully!" אם היא מחזירה True
    #       הדפיסו "Password change failed!" אם היא מחזירה False
    result = user.change_password("secure123", "newpass456")
    if result:
        print("Password changed successfully!")
    else:
        print("Password change failed!")
    
    # TODO: ודאו שהסיסמה החדשה עובדת באמצעות בדיקת "newpass456"
    #       הדפיסו "New password works!" אם היא מחזירה True
    #       הדפס "New password doesn't work!" אם היא מחזירה False
    if user.check_password("newpass456"):
        print("New password works!")
    else:
        print("New password doesn't work!")

elif test_case == "change_password_wrong_old_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    # TODO: נסה לשנות את הסיסמה באמצעות סיסמה ישנה שגויה
    #       הדפיסו "Security working: incorrect old password rejected!" אם היא מחזירה False
    #       הדפס "Security issue: password changed with wrong old password!" אם היא מחזירה True
    result = user.change_password("wrong", "hackerpw")
    if not result:
        print("Security working: incorrect old password rejected!")
    else:
        print("Security issue: password changed with wrong old password!")

elif test_case == "comprehensive_test":
    # TODO: צרו אובייקט User עם הסיסמה ההתחלתית "secure123"
    user = User("secure123")
    print("User created with initial password")
    
    # TODO: בדקו את המתודה check_password עם הסיסמה הנכונה
    if user.check_password("secure123"):
        print("Initial password verification successful")
    
    # TODO: בדוק את המתודה check_password עם סיסמה שגויה
    if not user.check_password("wrongpass"):
        print("Incorrect password properly rejected")
    
    # TODO: בדקו שינוי סיסמה מ-"secure123" ל-"newpass456"
    if user.change_password("secure123", "newpass456"):
        print("Password successfully changed")
    
    # TODO: ודא שהסיסמה הישנה כבר אינה תקפה
    if not user.check_password("secure123"):
        print("Old password no longer works")
    
    # TODO: ודא שהסיסמה החדשה תקפה
    if user.check_password("newpass456"):
        print("New password works correctly")
    
    # TODO: נסה לשנות את הסיסמה באמצעות סיסמה ישנה שגויה
    if not user.change_password("wrongold", "hackerpw"):
        print("Security maintained: wrong old password rejected")
    
    # TODO: ודא שהסיסמה עדיין מאובטחת לאחר ניסיון שינוי שנכשל
    if user.check_password("newpass456"):
        print("Password remains secure after failed change attempt")

else:
    print("Unknown test case")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Python אונליין