المزخرفات في بايثون – كيف نتعامل مع دوال بايثون بحُرية أكثر
المزخرفات في بايثون أو باللغة الانجليزية Decorators هي أحد أقوى وأجمل خصائص لغة البرمجة بايثون، ولكن البعض ينظر لها كموضوع يصعب الإلمام به أو فهمه بسهولة. المزخرفات هي موضوع مُتعلق بدوال بايثون، وتهدف إلى نقلك في التعامل مع الدوال إلى مستوى متقدم. أهلا بكم في بايثونات في هذا المقال الجديد الذي نشرح فيه أحد أشهر مواضيع البايثون وأكثرها ارتباكًا لدى البعض.
ما هي المزخرفات في بايثون ؟
المزخرفة في بايثون عبارة عن دالة يُمكن إسناد دالة أخرى لها كمُعطى. تعمل المزخرفة على تعديل الدالة المسندة إليها، وهذا يعني أنك عندما تستدعي دالة أُسندت لمزخرفة، فإنك تستدعي دالة تختلف في خصائصها عن الدالة الأصلية لها. حتى لا تبدأ عزيزي القارئ في الدخول في الإرباك المعهود في هذا الموضوع، من المهم أن نُراجع سويةً بعض المفاهيم الرئيسية في الدوال في البايثون والتي ستسهل علينا فهم الموضوع.
يجب أن نتذكر أن الدالة في بايثون عبارة عن كائن “object” وهذا يعني أنه يُمكن إسناد الدالة لمتغير ما كما في المثال التالي:
أو تمرير الدالة لدالة أخرى:
أو إرجاع الدالة كنتيجة لدالة أخرى:
من خلال الأمثلة الثلاثة السابقة، طبقنا 3 مفاهيم أساسية في التعامل مع الدوال ككائنات objects في البايثون:
- اسناد الدالة لمتغير.
- تمرير/إسناد الدالة كمُعطى.
- ارجاع الدالة كنتيجة لدالة أخرى.
دعونا نُطبق كافة المفاهيم الثلاثة السابقة في مثال واحد.
ما قبل المزخرفات في بايثون
سنُعرف دالة func1، بداخلها دالة func2، ويُمكن إسناد دالة لها func:
في المثال السابق، عرفنا الدالة func1 والتي يُمكن إسناد دالة لها باسم func، ثم بداخل الدالة func1 عرفنا دالة باسم func2 وبداخلها استدعينا الدالة المُسندة func (طبعنا قبلها وبعدها نصوص معينة)، ثم أرجعنا الدالة func2 كنتيجة للدالة func1.
بعد ذلك، عرفنا الدالة my_function وأسندناها للدالة func1 ثم أسندنا النتيجة المرجعة للمتغير باسم func. النتيجة المُرجعة عبارة عن دالة، نستدعي النتيجة كدالة بإضافة الأقواس لها، وستكون النتيجة كم يلي:
نُلاحظ أن نتيجة الدالة my_function سُبقت بنص Before func calling ثم تُليت بنص آخر After func calling. هذه النتيجة هي ما تقوم به المُزخرفة بالضبط، حيث نقوم بالتعديل على سلوك الدالة my_function التي تتضمن طباعة نص معين.
من خلال المثال السابق، نقول أن الدالة func2 مُحاطة wrapped بواسطة الدالة func1. الدالة func1 تلعب هنا دور المُزخرفة.
مثال المزخرفات في بايثون
اذًا فالمزخرفة عبارة عن دالة تُغير من سلوك دالة أخرى. سنُعدل على أسماء الدوال في المثال السابق لتكون أكثر وضوحًا وملائمةً:
المثال السابق (بعد تعديل أسماء الدوال) هو التطبيق العملي للمزخرفات في بايثون. ولكن، طريقة اسناد الدالة للمزخرفة ثم استدعاء النتيجة باضافة الأقواس، هي طريقة غريبة وغير واضحة كما يلي:
تُقدم البايثون هنا طريقةً أكثر وضوحًا باستخدام الرمز @، وهي الطريقة المُعتمدة في تعريف المزخرفات واستدعاء الدوال عليها. سنُعدل المثال السابق ليُصبح متوافقًا مع الطريقة المعتمدة لتعريف واستخدام المزخرفات في بايثون:
نُلاحظ أنه لاضافة المُزخرفة لدالة ما، نكتب في البداية الرمز @ ثم اسم الدالة التي تُمثل المزخرفة، وذلك قبل أول سطر في تعريف الدالة التي سنطبق المُزخرفة عليها. عند استدعاء الدالة المُطبق عليها المزخرفة، ستكون نتيجتها متغيرة وفقًا للتعديل الذي أضافته المزخرفة عليها.
تطبيقات عملية على المزخرفات في البايثون
بالمثال يتضح المقال.
بعد أن تعرفنا على كيفية بناء المزخرفة وتطبيقها على أي دالة نريد، يتبقى السؤال الدائم، والذي يسأله أي شخص، كيف يُمكننا الاستفادة من المزخرفات؟ وكيف يُمكن أن نُطبقها عمليًا في البرامج والتطبيقات التي نبنيها بلغة البايثون ؟
إليكم مثالين في موضوع المزخرفات:
قياس المدة الزمنية لتنفيذ دالة
في البداية نُعرف المزخرف باسم timeit، ثم نُعرف الدالة الداخلية باسم timed والتي تأخذ مُعطيات سيتم إسنادها للدالة التي نُريد زخرفتها وفق ما تتطلبه من عمل. نُعرف متغير باسم ts ونُسند له الوقت الذي بدأت فيه الدالة بالتنفيذ، ثم نستدعي الدالة المُزخرفة، وبعد أن تنتهي من تنفيذ الشيفرة البرمجية بداخلها، نُعرف متغير جديد باسم te ونُسند له الوقت الذي انتهت فيه الدالة المُزخرفة من العمل. بعد ذلك نطبع فرق الوقت بين te و ts والذي يُمثل الوقت الذي استغرقته الدالة المُزخرفة في التنفيذ.
الان يُمكننا استخدام المُزخرف timeit مع أي دالة، ولتوضيح عمل المزخرف، نُعرف الدالة sleepy واستخدمنا المزخرف معها. الدالة sleepy هي دالة بسيطة تقوم باستدعاء الدالة sleep من الوحدة المعيارية في بايثون time والتي تعمل على وقف التنفيذ حسب المدة التي نُمررها لها.
نتيجة تنفيذ المثال السابق ستكون كالتالي:
اضافة التسجيل لدوال بايثون
نحتاج في الكثير من التطبيقات التي نبنيها أن نُفعل خاصية التسجيل Logging في بايثون. لو أردنا أن نُراقب الدالة sleepy في المثال السابق، بحيث نحصل على ملف نصي يُمثل تاريخ وسجل استدعاء الدالة. يُمكن تنفيذ الفكرة من خلال المزخرفات.
المزخرف التالي أطلقنا عليه اسم log_decorator وسيعمل على إنشاء مُسجل باسم الدالة، وسيعمل المُسجل على تسجيل الأوقات التي تم استدعاء الدالة فيها بالاضافة إلى تسجيل المدة الزمنية التي استغرقتها الدالة في التنفيذ بالاضافة للنتيجة، وهذه الفكرة مهمة اذا أردنا مراقبة أداء التطبيق الذي نبنيه.
عند استخدام المُزخرف log_decorator على أي دالة، وعند استدعاء هذه الدالة، سنحصل على سجل استدعاء الدالة والنتيجة الخاصة بها. الشيفرة البرمجية التالية نستخدم log_decorator في مراقبة الدالة double_function و sleepy:
عند الاستدعاء، سيتم انشاء ملفين نصيين، كل ملف خاص بكل دالة، وسنبدأ بالحصول على السجلات log records مع كل استدعاء:
الملف double_functio.log:
الملف sleepy.log:
المثال بالشيفرة البرمجية كاملة:
إلى هنا نكون قد إنتهينا من شرح موضوع المزخرفات في بايثون، ونأمل أن نكون وفقنا في ذلك. اذا وجدت أن المقال مفيد، شاركه مع أصدقاءك المبرمجين، أو الذي يرغبون بمعرفة المزيد عن المزخرفات. ونسعد بتفاعلكم معنا عبر منصات التواصل الاجتماعي.