Menu
CoddyTech
flag Ar iconالعربيةdown icon

Property Decorators متقدمة

جزء من قسم البرمجة كائنية التوجه في رحلة 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 صياغة تفكيك الصفوف: 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  # ليست مجموعة مرتبة
    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 عبر الإنترنت