مزخرفات 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).
التحدي
متوسطفي هذا التحدي، ستقوم بتنفيذ مزخرف فئة (class decorator) يضيف وظيفة تتبع استدعاءات الدوال.
decorator.py- قم بتنفيذ مزخرف الفئةadd_countercalculator.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}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال8مفاهيم OOP المتقدمة
التركيب مقابل الوراثةMixinsدوال Static و Classمزخرفات Classمديرو السياق (Context Managers)