مزخرفات Class
جزء من قسم البرمجة كائنية التوجه في رحلة Python على Coddy. الدرس 40 من 64.
تتيح لك مزخرفات الفئات تعديل الفئات أو تحسينها من خلال تغليفها بدالة أخرى. وهي تعمل مثل مزخرفات الدوال، لكنها تُطبَّق على الفئات بأكملها.
إليك فئة بسيطة بدون تزيين:
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}"الآن، يحتوي الصنف على both original وmethods المضافة:
person = EnhancedPerson("Alice")
print(person.greet()) # مرحباً، اسمي Alice
print(person.farewell()) # وداعاً من Aliceيمكنك أيضًا تغليف method موجودة: تخزين original حتى تتمكن من استدعائها داخل الغلاف. هذا مفيد عندما تريد إضافة behaviour (مثل التتبّع أو التسجيل) حول 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
# مرحباً، اسمي Aliceالخطوات الأساسية لتغليف method موجودة هي:
1. احفظ method الأصلية: original = cls.method
2. Define function جديدة تستدعي original(self) بالإضافة إلى أي منطق إضافي.
3. أسند function الجديدة مرة أخرى إلى class: cls.method = new_function
4. أعد class المعدّلة.
نقطة أساسية: تأخذ مُزَيِّنات class class كـ input، وتعدّلها أو تعزّزها، وتعيد class modified. يمكنها add methods، أو تعديل السمات، أو تغليف functionality الحالية. استخدم @decorator_name فوق class definition لتطبيقها. يوفّر هذا طريقة واضحة لتوسيع functionality الخاصة بـ class دون استخدام الوراثة.
التحدي
متوسطفي هذا التحدي، ستنفّذ مُزيّن class يضيف وظيفة تتبّع استدعاءات method.
decorator.py- نفّذ مُزيّنadd_counterلـclasscalculator.py- يحتوي على الفئات التي ستستخدم مُزيّنك
يحتوي الملف driver.py على سيناريوهات اختبار شاملة ستتحقق من تنفيذك باستخدام حالات استخدام وأنماط توريث وحالات طرفية متنوعة. لا تحتاج إلى تعديل هذا الملف، ولكن فحصه سيساعدك على فهم behaviour المتوقع.
جرّب بنفسك
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) # Should print {'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) # Should print {'add': 2, 'subtract': 1}
print(calc2.call_counts) # Should print {'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) # Should print {'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) # Should print {'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) # Should print {'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) # Should print {'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) # Should print {'add': 1, 'subtract': 1}
elif test_case == "counter_reset":
calc = Calculator()
print(calc.add(5, 3)) # يجب أن يطبع 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# أعد تعيين العداد
calc.call_counts = {"add": 0, "subtract": 0}
print(calc.add(2, 7)) # يجب أن يطبع 9
print(calc.call_counts) # Should print {'add': 0, 'subtract': 0}
elif test_case == "counter_manipulation":
calc = Calculator()
print(calc.add(5, 3)) # يجب أن يطبع 8
print(calc.call_counts) # Should print {'add': 1, 'subtract': 0}
# تلاعب بالعداد مباشرة
calc.call_counts["add"] = 100
print(calc.add(2, 7)) # يجب أن يطبع 9
print(calc.call_counts) # Should print {'add': 101, 'subtract': 0}
elif test_case == "performance_test":
calc = Calculator()
# نفّذ 1000 عملية add
for i in range(1000):
calc.add(i, i+1)
# نفّذ 500 عملية subtract
for i in range(500):
calc.subtract(i+10, i)
print(calc.call_counts) # Should print {'add': 1000, 'subtract': 500}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال8مفاهيم OOP المتقدمة
التركيب مقابل الوراثةMixinsدوال Static و Classمزخرفات Classمديرو السياق (Context Managers)تدرّب بنفسك: مترجم Python عبر الإنترنت