תכונות פרטיות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 (מתודות גישה) כדי לעבוד כראוי עם נתונים פרטיים ולהוסיף אימות. ״הקוד הנוסף״ מונע באגים בכך שהוא מבטיח שהנתונים תמיד תקינים.
אתגר
קלהשלם את המחלקה 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")השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםהפרמטר selfמתודותמאפייניםשיטת הבנאי (__init__)חזרה: מחשבון פשוט5פולימורפיזם
בחינה מחדש של דריסת מתודותטיפוסיות ברווזמחלקות ומתודות אבסטרקטיותעיצוב ממשקיםסיכום – מחשבון צורות8מושגים מתקדמים בתכנות מונחה עצמים
קומפוזיציה לעומת הורשהMixinsמתודות סטטיות ומתודות מחלקהדקורטורים למחלקותמנהלי הקשרתרגלו בעצמכם: קומפיילר Python אונליין