مستوى المقال: محترف (Advanced) — يفترض إنك مرتاح مع FastAPI أو Flask، وعارف إيه هي الـ closures، وكتبت قبل كده middleware في Python.
لو فريقك بيكرّر نفس 8 سطور لـ logger.info() و check_permission() و audit_event() في كل endpoint من 240 endpoint عندك، انت بتدفع 1920 سطر boilerplate. أي تعديل في الـ logging schema بيتطلب تعديل 240 ملف، وأي مهندس جديد لازم يفتكر يحطّ السطور دي يدويًا. Decorator واحد فوق الـ function بيشيل المشكلة دي، ويخلّي القاعدة قابلة للتطبيق automatically.
المشكلة باختصار
الـ cross-cutting concerns — يعني الكلام اللي بيتكرر في كل endpoint زي logging و authentication و rate limiting و request tracing — بتنتشر في كل الكود لو ما تعاملناش معاها بشكل مركزي. بدل ما نعتمد على إن كل مهندس يفتكر، نخليها تتطبّق automatically على أي function عليها decorator. ده بالظبط الفرق بين كود فيه discipline وكود فيه enforcement.
المثال البسيط أولًا: حارس البنك
تخيل بنك فيه 30 موظف. كل واحد فيهم لمّا الزبون يدخل عنده، المفروض يطلب البطاقة، يكتب رقمها في دفتر، ويتأكد إن الزبون مش في القائمة السوداء. ده 3 خطوات لازم يعملهم كل موظف قبل أي عملية. لو خلّيت كل موظف يعملهم بنفسه، ساعات بينسوا، وساعات بيكتبوا الرقم غلط، وساعات بيتجاهلوا القائمة السوداء عشان مستعجلين.
الحل العملي: تحطّ حارس واحد على باب كل موظف. الزبون لازم يعدّي على الحارس الأول، الحارس بيعمل الـ 3 خطوات، وبعدين بيمرّر الزبون للموظف. الموظف يركّز على شغله الأصلي بس. لو غيّرت سياسة التحقق، بتغيّرها في الحارس، مش في 30 موظف.
الـ Decorator بالظبط هو الحارس ده. بيقف قدام الـ function، بيعمل الكلام المشترك، وبعدين بيستدعي الـ function الأصلية.
التعريف العلمي: Higher-Order Functions و Closures
على المستوى الرسمي، الـ Decorator في Python هو higher-order function بياخد function كـ input و بيرجّع function تانية. التعريف ده موثّق في PEP 318 (سنة 2003) ومبني على مفهوم الـ closures: الـ function اللي بترجع بتفضل ماسكة reference لـ scope الـ function الأصلية، حتى بعد ما الـ scope ده يخلص.
السكنتاكس @decorator فوق الـ function هو مجرد syntactic sugar للـ func = decorator(func). النتيجة إن الـ name اللي انت معرّفه بيشاور على الـ wrapper، مش على الـ function الأصلية. عشان كده بنستخدم functools.wraps عشان نحافظ على الـ __name__ و __doc__ و الـ signature بتاع الـ function الأصلية، عشان أدوات زي Sphinx و FastAPI تقدر تقرأها صح.
الكود الفعلي: Audit Logging على FastAPI في 38 سطر
الكود ده بيشتغل على Python 3.12 و FastAPI 0.110+. بيستخدم contextvars عشان يحفظ request_id عبر async boundaries بدون global state غلط: