المُزخرفات
Decorators
المُزخرفات — Decorators
المُزخرفات هي واحدة من أكثر أدوات Python قوةً وأناقةً. تجدها في كل مكتبة Python ناضجة — Flask يستخدمها لتعريف المسارات (@app.route), Django لصلاحيات المستخدم (@login_required), وdataclasses نفسها مُزخرف. فهمها يفتح لك باباً لكتابة كود أكثر نظافة وأقل تكراراً.
الدوال كقيم من الدرجة الأولى
قبل أن نتحدث عن المُزخرفات، يجب أن تفهم مبدأً أساسياً في Python: الدوال قيم مثل الأعداد والنصوص. يمكن تمريرها كمعاملات، تخزينها في متغيرات، وإرجاعها من دوال أخرى.
الدالة make_multiplier تُنشئ دالة جديدة في كل استدعاء وتُرجعها. هذه الدالة الداخلية تتذكر قيمة factor حتى بعد خروج make_multiplier — هذا يسمى Closure (إغلاق).
مُزخرف بسيط — Simple Decorator
المُزخرف في جوهره هو دالة تأخذ دالة وتُرجع دالة. تُضيف سلوكاً قبل أو بعد الدالة الأصلية دون تعديلها.
*args و**kwargs تجعل wrapper مرنة — تقبل أي عدد من المعاملات وأي معاملات مسماة، وتُمررها للدالة الأصلية كما هي.
صياغة @ — The @ Syntax
Python توفر صياغة مختصرة باستخدام @ تجعل الكود أكثر وضوحاً. ما فعلناه يدوياً (add = log_call(add)) يعادل تماماً:
@log_call قبل تعريف greet يعادل كتابة greet = log_call(greet) بعد التعريف. الصياغة بـ@ أكثر وضوحاً لأن المُزخرف يظهر مع تعريف الدالة مباشرةً.
مُزخرف مع معاملات — Decorator Factory
أحياناً تريد مُزخرفاً قابلاً للإعداد — مثل @retry(times=3) أو @cache(ttl=60). هنا تحتاج decorator factory: دالة تُنتج مُزخرفاً.
لاحظ الطبقات الثلاث:
repeat(times)— تأخذ الإعداد وتُرجع مُزخرفاًdecorator(fn)— المُزخرف الفعلي، يأخذ الدالة ويُرجع wrapperwrapper(*args, **kwargs)— الدالة المُعدَّلة الفعلية
@repeat(times=3) يعادل: أولاً يستدعي repeat(3) للحصول على مُزخرف، ثم يُطبّق ذلك المُزخرف على say.
functools.wraps — حفظ البيانات الوصفية
مشكلة صامتة تحدث مع المُزخرفات: wrapper تحلّ محل الدالة الأصلية، فتضيع اسمها ووثائقها.
functools.wraps ضروري في أي مُزخرف تكتبه. بدونه يفشل help() ويتعطّل inspect، وتختفي وثائقك.
مُزخرفات متعددة — Stacking Decorators
يمكن تطبيق أكثر من مُزخرف على دالة واحدة. تُطبَّق من الأقرب للدالة إلى الأبعد (من الأسفل للأعلى):
مُزخرف عملي — Timing Decorator
متى تستخدم المُزخرفات؟
المُزخرفات مناسبة عندما يكون لديك سلوك متكرر ينطبق على دوال متعددة، ومستقل عن منطقها الرئيسي. أمثلة شائعة:
- Logging — تسجيل كل استدعاء ومعاملاته
- Timing — قياس الأداء
- Caching — حفظ النتائج لتجنب إعادة الحساب (
functools.lru_cache) - Auth — التحقق من صلاحية المستخدم قبل تنفيذ الدالة
- Retry — إعادة المحاولة عند الفشل (ما ستبنيه في الدرس الأخير)