Menu
CoddyTech

דקורטורים מתקדמים של Property

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

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

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

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    @property
    def area(self):
        return self.width * self.height
    
    @property
    def perimeter(self):
        return 2 * (self.width + self.height)

יש להשתמש במאפיינים מחושבים כמו בתכונות רגילות:

rect = Rectangle(5, 3)
print(rect.area)      # 15 - מחושב אוטומטית
print(rect.perimeter) # 16 - מחושב אוטומטית

צרו מאפיין עם getter, setter ו-deleter:

class Temperature:
    def __init__(self):
        self._temp = 0
    
    @property
    def temperature(self):
        return self._temp
    
    @temperature.setter
    def temperature(self, value):
        if value < -273.15:
            raise ValueError("Temperature below absolute zero!")
        self._temp = value
    
    @temperature.deleter
    def temperature(self):
        print("Resetting temperature to 0")
        self._temp = 0

השתמשו במלוא הפונקציונליות של המאפיין:

temp = Temperature()

השתמשו במגדיר עם אימות:

temp.temperature = 25
print(temp.temperature)  # 25

# temp.temperature = -300  # יגרום ל-ValueError

השתמשו במוחק:

del temp.temperature
print(temp.temperature)  # 0

צרו דוגמה מורכבת יותר עם ניקוד במשחק:

class Player:
    def __init__(self, name):
        self.name = name
        self._score = 0
        self._level = 1
    
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        if value >= 0:
            self._score = value
            self._level = (value // 1000) + 1
        else:
            raise ValueError("Score cannot be negative")
    
    @score.deleter
    def score(self):
        print(f"Resetting {self.name}'s progress")
        self._score = 0
        self._level = 1
    
    @property
    def level(self):
        return self._level

player = Player("Alice")
player.score = 2500
print(f"Score: {player.score}, Level: {player.level}")  # ניקוד: 2500, רמה: 3

del player.score
print(f"Score: {player.score}, Level: {player.level}")  # ניקוד: 0, רמה: 1

פלט:

15
16
25
Resetting temperature to 0
0
Score: 2500, Level: 3
Resetting Alice's progress
Score: 0, Level: 1

נקודה מרכזית: דקורטורים מתקדמים של מאפיינים מאפשרים מאפיינים מחושבים (המחושבים מנתונים אחרים), מחיקת מאפיינים באמצעות @property.deleter, ושליטה מלאה בקבלת מאפיינים, בהגדרתם ובמחיקתם. כך נוצרים ממשקים אינטואיטיביים תוך שמירה על אימות נתונים חזק ואנקפסולציה.

challenge icon

אתגר

בינוני

באתגר הזה תממשו מחלקה בשם Rectangle עם כימוס מתאים ואימות מאפיינים.

  • rectangle.py - זהו הקובץ שעליכם לערוך, והוא מכיל הערות TODO שינחו אתכם במימוש
  • driver.py - מכיל תרחישי בדיקה נרחבים (אין לשנות אותו)
  1. שמרו את הערכים במאפיינים המוגנים (הפנימיים) _width ו-_height
  2. צרו מאפיינים עבור width ו-height עם אימות (הערכים חייבים להיות חיוביים)
    • העלו הודעות מתאימות של ValueError כפי שמצוין בהערות TODO
    • חשוב: בדקו אם הערך קטן מ-0 או שווה לו, ואז העלו את השגיאה (כך תובטח התנהגות עקבית)
  3. ממשו מאפיינים לקריאה בלבד עבור area ו-perimeter
  4. צרו מאפיין dimensions עם פונקציונליות של getter, setter ו-deleter, כפי שמתואר בהערות TODO
    • חשוב: ב-setter השתמשו בתחביר פירוק של tuple: width, height = dimensions

נסו בעצמכם

from rectangle import Rectangle

# מטפל במקרי בדיקה
test_case = input()

# בדיקת פונקציונליות בסיסית
if test_case == "default_test":
    rect = Rectangle(5, 3)
    print(f"Width: {rect.width}, Height: {rect.height}")
    print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
    
    # בדיקת המאפיין dimensions
    print(f"Dimensions: {rect.dimensions}")
    rect.dimensions = (10, 8)
    print(f"New area: {rect.area}")
    
    # בדיקת אימות ערכים
    try:
        rect.width = -2
    except ValueError as e:
        print(f"Validation error: {e}")
    
    # בדיקת פעולת המחיקה
    del rect.dimensions
    print(f"After reset: {rect.dimensions}")

# בדיקה עם ערכי אפס
elif test_case == "zero_values":
    try:
        rect = Rectangle(0, 5)
    except ValueError as e:
        print(f"Error creating rectangle: {e}")
    
    try:
        rect = Rectangle(5, 0)
    except ValueError as e:
        print(f"Error creating rectangle: {e}")

# בדיקה עם ערכים שליליים
elif test_case == "negative_values":
    rect = Rectangle(5, 3)
    original_dimensions = rect.dimensions
    
    try:
        rect.dimensions = (5, -3)
    except ValueError as e:
        print(f"Error setting dimensions: {e}")
    
    print(f"Dimensions after failed update: {rect.dimensions}")
    print(f"Original dimensions preserved: {rect.dimensions == original_dimensions}")

# בדיקה עם ערכים גדולים
elif test_case == "large_values":
    rect = Rectangle(1000000, 2000000)
    print(f"Large rectangle area: {rect.area}")
    print(f"Large rectangle perimeter: {rect.perimeter}")

# בדיקה עם ערכי נקודה צפה
elif test_case == "float_values":
    rect = Rectangle(3.5, 2.75)
    print(f"Dimensions: {rect.dimensions}")
    print(f"Area: {rect.area}")
    print(f"Perimeter: {rect.perimeter}")
    
    rect.dimensions = (1.1, 2.2)
    print(f"New area with float dimensions: {rect.area}")

# בדיקה עם שגיאות טיפוס
elif test_case == "type_errors":
    try:
        rect = Rectangle("5", 3)
    except Exception as e:
        print(f"Type error during creation: {type(e).__name__}: {e}")
    
    rect = Rectangle(5, 3)
    try:
        rect.dimensions = 10  # לא tuple
    except Exception as e:
        print(f"Type error setting dimensions: {type(e).__name__}: {e}")

# בדיקת פעולות מרובות על מאפיינים
elif test_case == "property_operations":
    rect = Rectangle(5, 10)
    print(f"Initial - Width: {rect.width}, Height: {rect.height}, Area: {rect.area}")
    
    rect.width = 8
    print(f"After width change - Width: {rect.width}, Area: {rect.area}")
    
    rect.height = 6
    print(f"After height change - Height: {rect.height}, Area: {rect.area}")
    
    rect.dimensions = (12, 9)
    print(f"After dimensions change - Dimensions: {rect.dimensions}, Area: {rect.area}")
    
    del rect.dimensions
    print(f"After reset - Dimensions: {rect.dimensions}, Area: {rect.area}")

# בדיקת מקרי קצה באימות מאפיינים
elif test_case == "validation_edge_cases":
    rect = Rectangle(5, 3)
    
    try:
        rect.width = 0
    except ValueError as e:
        print(f"Zero width error: {e}")
    
    # יש לקבל ערך חיובי קטן מאוד
    rect.height = 0.0001
    print(f"Small height accepted: {rect.height}")
    print(f"Area with small height: {rect.area}")

# בדיקת מלבנים מרובים
elif test_case == "multiple_rectangles":
    rect1 = Rectangle(5, 3)
    rect2 = Rectangle(10, 2)
    rect3 = Rectangle(4, 4)
    
    print(f"Rectangle 1 - Area: {rect1.area}, Perimeter: {rect1.perimeter}")
    print(f"Rectangle 2 - Area: {rect2.area}, Perimeter: {rect2.perimeter}")
    print(f"Rectangle 3 - Area: {rect3.area}, Perimeter: {rect3.perimeter}")
    
    # שינוי כל מלבן
    rect1.width = 7
    rect2.height = 5
    rect3.dimensions = (6, 6)
    
    print(f"After modifications:")
    print(f"Rectangle 1 - Dimensions: {rect1.dimensions}")
    print(f"Rectangle 2 - Dimensions: {rect2.dimensions}")
    print(f"Rectangle 3 - Dimensions: {rect3.dimensions}")

# בדיקת ביצועים עם פעולות רבות
elif test_case == "performance_test":
    rect = Rectangle(5, 5)
    
    # ביצוע פעולות רבות על מאפיינים
    for i in range(1000):
        rect.width = i % 10 + 1  # ערכים 1-10
        rect.height = i % 5 + 1  # ערכים 1-5
        area = rect.area  # גישה למאפיין מחושב
        perimeter = rect.perimeter  # גישה למאפיין מחושב
        dims = rect.dimensions  # גישה לפונקציית הקבלה של המאפיין
    
    print(f"Final state after 1000 operations:")
    print(f"Width: {rect.width}, Height: {rect.height}")
    print(f"Area: {rect.area}, Perimeter: {rect.perimeter}")
quiz iconבחנו את עצמכם

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

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

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