لو الكود بتاعك فيه 14 دالة API call ومحتاج كل واحدة فيهم تعمل retry على فشل الشبكة + log + قياس الزمن، الكوبي-بيست بتاع الـ try/except في كل دالة هيخلّيك تعيد كتابة 280 سطر. Decorator واحد في 22 سطر بيغطّيهم كلهم بـ @retry فوق التوقيع، بدون لمس جسم الدالة، وبدون كسر الـ type hints.
المستوى: متوسط — الشرح ده بيفترض إنك مرتاح مع function definitions في Python، مفهوم first-class functions، والـ *args و **kwargs. مش محتاج تعرف Decorators من قبل، هنبني من الصفر بمثال واقعي قبل التعريف العلمي.
Decorators في Python: غلّف دوالك بسلوك مشترك بسطر واحد
المشكلة باختصار
تخيّل خدمة Python بتنادي 14 endpoint خارجي: Stripe، SendGrid، Slack، خدمات داخلية. كل دالة منهم محتاجة 3 سلوكيات متكررة: retry لو الشبكة فشلت، log للزمن، و حفظ النتيجة في cache لمدة 60 ثانية. الطريقة المباشرة بتقول: حط الكود ده جوّا كل دالة. النتيجة 280 سطر مكرّر، و bugs بتظهر بعد 3 شهور لما تكتشف إن طريقة الـ retry في الـ webhook handler طلعت مختلفة عن طريقة retry في الـ billing service.
Decorator واحد بتكتبه مرة، وبيتطبّق عليهم كلهم بسطر واحد فوق كل دالة. ده مش مجرد syntactic sugar، ده تطبيق مباشر لمبدأ functions كـ first-class objects اللي Python مبنية عليه.
المفهوم بمثال واقعي قبل التعريف العلمي
تخيّل إن عندك مهندس صيانة في عمارة كبيرة. كل مهمة بيستلمها — إصلاح ماسورة، تركيب مفتاح كهرباء، تنظيف خزّان — محتاجة 3 خطوات ثابتة قبل وبعد: يلبس قفازات، يصوّر الحالة قبل البداية، ويسلّم تقرير في الآخر. عوضًا عن إنك تكتب الـ 3 خطوات دي في وصف كل مهمة، انت بتجيب موظف استقبال يلفّ المهمة كلها: ياخدها، يلبس القفازات، يبدأ التصوير، يدخلها للمهندس، يستنّى النتيجة، يكتب التقرير، يسلّمها. المهندس مش لازم يعرف إن موظف الاستقبال موجود أصلاً، وهو شغّال على نفس المهمة بنفس الخطوات.
الـ decorator في Python هو موظف الاستقبال ده بالظبط. هو function بتاخد function تانية كـ input، وبترجّع function جديدة بتعمل شغل قبل و/أو بعد استدعاء الأصلية. الدالة الأصلية ما اتغيّرتش، بس استدعاءها بقى يمر من غلاف.
التعريف العلمي الدقيق
بحسب توثيق Python الرسمي في Python Language Reference §8.7 و PEP 318 اللي قنّن الـ syntax في 2003، الـ decorator هو callable بياخد callable واحد على الأقل ويرجّع callable. الكتابة:
@my_decorator
def my_function():
passهي اختصار مكافئ تمامًا لـ: