Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

مزخرفات Class

جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 40 من 64.

تسمح لك مزخرفات الفئات (Class decorators) بتعديل أو تحسين الفئات من خلال تغليفها بدالة أخرى. وهي تعمل مثل مزخرفات الدوال ولكن يتم تطبيقها على فئات كاملة.

إليك فئة بسيطة بدون مزخرفات:

class Person:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

أنشئ مزخرف فئة (class decorator) يضيف دالة (method) جديدة:

def add_farewell(cls):
    def farewell(self):
        return f"Goodbye from {self.name}"
    
    cls.farewell = farewell  # إضافة الدالة إلى الفئة
    return cls

قم بتطبيق المزخرف (decorator) على الفئة باستخدام @:

@add_farewell
class EnhancedPerson:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

الآن تمتلك الفئة كلاً من الدوال الأصلية والمضافة:

person = EnhancedPerson("Alice")
print(person.greet())     # Hello, my name is Alice
print(person.farewell())  # Goodbye from Alice

يمكنك أيضاً تغليف طريقة (method) موجودة — من خلال تخزين الطريقة الأصلية حتى تتمكن من استدعائها داخل الغلاف. هذا مفيد عندما تريد إضافة سلوك (مثل التتبع أو التسجيل) حول طريقة تمتلكها الفئة (class) بالفعل:

def add_tracking(cls):
    original_greet = cls.greet  # تخزين الطريقة الأصلية

    def tracked_greet(self):
        print(f"greet was called")   # سلوك إضافي قبل
        return original_greet(self)  # استدعاء الطريقة الأصلية

    cls.greet = tracked_greet  # استبدال الطريقة في الفئة
    return cls

@add_tracking
class TrackedPerson:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, my name is {self.name}"

عندما تستدعي greet الآن، يتم تشغيل الغلاف أولاً، ثم يتم التفويض إلى الدالة الأصلية:

person = TrackedPerson("Alice")
print(person.greet())
# تم استدعاء greet
# Hello, my name is Alice

الخطوات الأساسية لتغليف تابع (method) موجود هي:
1. حفظ التابع الأصلي: original = cls.method
2. تعريف دالة جديدة تستدعي original(self) بالإضافة إلى أي منطق إضافي.
3. إسناد الدالة الجديدة مرة أخرى إلى الفئة (class): cls.method = new_function
4. إرجاع الفئة المعدلة.

نقطة رئيسية: تأخذ مزخرفات الفئات (Class decorators) فئة كمدخل، وتقوم بتعديلها أو تحسينها، ثم تعيد الفئة المعدلة. يمكنها إضافة دوال (methods)، أو تعديل سمات (attributes)، أو تغليف الوظائف الحالية. استخدم @decorator_name فوق تعريف الفئة لتطبيقها. يوفر هذا طريقة نظيفة لتوسيع وظائف الفئة دون الحاجة إلى الوراثة (inheritance).

challenge icon

التحدي

متوسط

في هذا التحدي، ستقوم بتنفيذ مزخرف فئة (class decorator) يضيف وظيفة تتبع استدعاءات الدوال.

  • decorator.py - قم بتنفيذ مزخرف الفئة add_counter
  • calculator.py - يحتوي على الفئات التي ستستخدم المزخرف الخاص بك

يحتوي ملف driver.py على سيناريوهات اختبار شاملة ستتحقق من صحة تنفيذك مقابل حالات استخدام متنوعة، وأنماط الوراثة، والحالات الاستثنائية. لا تحتاج إلى تعديل هذا الملف، ولكن فحصه سيساعدك على فهم السلوك المتوقع.

ورقة مرجعية

تقوم مزخرفات الفئات (Class decorators) بتعديل أو تحسين الفئات من خلال تغليفها بدالة أخرى. فهي تأخذ فئة كمدخل، وتعدلها، ثم تعيد الفئة المعدلة.

الصيغة الأساسية لمزخرف الفئة:

def decorator_name(cls):
    # تعديل الفئة
    return cls

@decorator_name
class MyClass:
    pass

مثال - إضافة دالة (method) إلى فئة:

def add_farewell(cls):
    def farewell(self):
        return f"Goodbye from {self.name}"
    
    cls.farewell = farewell  # إضافة الدالة إلى الفئة
    return cls

@add_farewell
class EnhancedPerson:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        return f"Hello, my name is {self.name}"

person = EnhancedPerson("Alice")
print(person.greet())     # Hello, my name is Alice
print(person.farewell())  # Goodbye from Alice

مثال - تغليف دالة موجودة (حفظ الأصلية، ثم استبدالها):

def loud_greet(cls):
    original_greet = cls.greet  # حفظ الدالة الأصلية

    def new_greet(self):
        result = original_greet(self)  # استدعاء الأصلية
        return result.upper()          # تحسين النتيجة

    cls.greet = new_greet  # الاستبدال بالنسخة المغلفة
    return cls

@loud_greet
class EnhancedPerson:
    def __init__(self, name):
        self.name = name

    def greet(self):
        return f"Hello, my name is {self.name}"

person = EnhancedPerson("Alice")
print(person.greet())  # HELLO, MY NAME IS ALICE

الخطوات الأساسية لتغليف دالة موجودة:

original = cls.method_name — حفظ الدالة الأصلية
def new_method(self): original(self) — استدعاؤها داخل المغلف
cls.method_name = new_method — استبدال الدالة في الفئة

يمكن لمزخرفات الفئات إضافة دوال، أو تعديل سمات، أو تغليف وظائف موجودة دون استخدام الوراثة.

جرّب بنفسك

from calculator import Calculator

# معالج حالات الاختبار الشامل
test_case = input()

if test_case == "basic_functionality":
    calc = Calculator()
    print(calc.add(5, 3))  # يجب أن يطبع 8
    print(calc.add(2, 7))  # يجب أن يطبع 9
    print(calc.subtract(10, 4))  # يجب أن يطبع 6
    print(calc.call_counts)  # يجب أن يطبع {'add': 2, 'subtract': 1}

elif test_case == "multiple_instances":
    calc1 = Calculator()
    calc2 = Calculator()
    
    print(calc1.add(5, 3))  # يجب أن يطبع 8
    print(calc2.add(2, 7))  # يجب أن يطبع 9
    print(calc1.subtract(10, 4))  # يجب أن يطبع 6
    
    # يجب أن تشترك كلتا النسختين في نفس call_counts
    print(calc1.call_counts)  # يجب أن يطبع {'add': 2, 'subtract': 1}
    print(calc2.call_counts)  # يجب أن يطبع {'add': 2, 'subtract': 1}
    print(calc1.call_counts is calc2.call_counts)  # يجب أن يطبع True

elif test_case == "method_call_order":
    calc = Calculator()
    
    print(calc.add(1, 2))  # يجب أن يطبع 3
    print(calc.subtract(5, 2))  # يجب أن يطبع 3
    print(calc.add(10, 20))  # يجب أن يطبع 30
    print(calc.add(7, 3))  # يجب أن يطبع 10
    print(calc.call_counts)  # يجب أن يطبع {'add': 3, 'subtract': 1}

elif test_case == "large_values":
    calc = Calculator()
    
    print(calc.add(1000000, 2000000))  # يجب أن يطبع 3000000
    print(calc.subtract(5000000, 2000000))  # يجب أن يطبع 3000000
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 1}

elif test_case == "negative_values":
    calc = Calculator()
    
    print(calc.add(-5, -3))  # يجب أن يطبع -8
    print(calc.subtract(-10, -4))  # يجب أن يطبع -6
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 1}

elif test_case == "float_values":
    calc = Calculator()
    
    print(calc.add(5.5, 3.2))  # يجب أن يطبع 8.7
    print(calc.subtract(10.5, 4.2))  # يجب أن يطبع 6.3
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 1}

elif test_case == "zero_values":
    calc = Calculator()
    
    print(calc.add(0, 0))  # يجب أن يطبع 0
    print(calc.subtract(0, 0))  # يجب أن يطبع 0
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 1}

elif test_case == "counter_reset":
    calc = Calculator()
    
    print(calc.add(5, 3))  # يجب أن يطبع 8
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 0}
    
    # إعادة تعيين العداد
    calc.call_counts = {"add": 0, "subtract": 0}
    
    print(calc.add(2, 7))  # يجب أن يطبع 9
    print(calc.call_counts)  # يجب أن يطبع {'add': 0, 'subtract': 0}

elif test_case == "counter_manipulation":
    calc = Calculator()
    
    print(calc.add(5, 3))  # يجب أن يطبع 8
    print(calc.call_counts)  # يجب أن يطبع {'add': 1, 'subtract': 0}
    
    # التلاعب بالعداد مباشرة
    calc.call_counts["add"] = 100
    
    print(calc.add(2, 7))  # يجب أن يطبع 9
    print(calc.call_counts)  # يجب أن يطبع {'add': 101, 'subtract': 0}

elif test_case == "performance_test":
    calc = Calculator()
    
    # إجراء 1000 عملية جمع
    for i in range(1000):
        calc.add(i, i+1)
    
    # إجراء 500 عملية طرح
    for i in range(500):
        calc.subtract(i+10, i)
    
    print(calc.call_counts)  # يجب أن يطبع {'add': 1000, 'subtract': 500}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming