البحث في الموقع
المحتوى عن 'مزخرفات'.
-
المُزخرفات من أعظم مميزات لغة بايثون، إذ تساعدك على بناء برنامجك باحترافية أكثر موفرة طريقة بسيطة لإضافة خاصيات جديدة للدالة. وهي ببساطة دوال تستطيع أن تعدل على دوال أخرى. تذكير ببعض المفاهيم الأساسية إذا لم تكن تعرف شيئا عن الدوال في لغة بايثون فيجب عليك العودة للدرس السابق الدوال في بايثون قبل أن تكمل قراءة هذا الدرس. تُعتبر الدوال في لغة بايثون كائنات من نوع الفئة الأولى أو First class objects. ما يعني أنّنا نستطيع القيام بالعديد من العمليات، وهي كالتالي: يُمكنك تعريف دالة داخل دالة أخرى يُمكنك أن تستدعي دالة داخل أخرى يُمكنك أن تقوم بتمرير دالة كمُعامل لدالة أخرى يُمكنك أن تُسند دالة لمُتغير يُمكنك إرجاع دالة داخل دالة أخرى بما أنّ المُزخرفات مُجرّد دوال فعلينا أن نبدأ من الأساس، لاحظ الدالة التالية: def say_hello(): print 'Hello!' عند استدعاء الدالة ستُطبع القيمة Hello على الشاشة، هذا ليس بالأمر المُعقد، الآن ماذا لو أردنا أن نعدل الدالة في جزء آخر من البرنامج لكي تطبع أشياء أخرى وتؤدي أغراضا أخرى قبل استدعاء الدالة أو بعد ذلك؟ يُمكن أن نعدّل الدالة مُباشرة، لكن هذا الأمر سيغير من طريقة سير البرنامج، إذ نريد أن نعدل الدالة في منطقة واحدة فقط من البرنامج وليس في كامل البرنامج، هذه المسألة تُحل بالمُزخرفات، وكما يدل اسمها فهي دوال تُزيّن وتُزخرف الدالة الأصلية، أي تُضيف عليها مهاما أخرى. سننشئ لهذه الدالة الآن مُزخرفا Decorator يقوم بطباعة Before قبل تنفيذ الدالة و After بعد تنفيذ الدالة، وذلك دون تعديل الدالة مُباشرة. انظر المثال التالي: def decorator(function): def function_decorator(): print 'Before' function() print 'After' return function_decorator الشيفرة أعلاه عبارة عن دالة تقبل دالة أخرى (الدالة التي نرغب بزَخرَفَتِها أو تزيينها) كمُعامل وبعدها نقوم بإنشاء دالة داخل هذه الدالة لطباعة القيمة Before ثم استدعاء الدالة الأصلية (المُعامل) بعدها طباعة After وأخيرا إرجاع الدالة الجديدة (وهي نُسخة مزخرفة من الدالة الأصلية). بعدها يُمكننا أن نستخدم هذا المُزخرف لزخرفة أي دالة مهما كانت، انظر المثال التالي: # -*- coding: utf-8 -*- def decorator(function): # إنشاء الدالة المسؤولة عن الزخرفة def function_decorator(): # إنشاء الدالة التي ستكون نسخة مزخرفة من الدالة المُمرّرة في كمُعامل print 'Before' # طباعة جملة قبل تنفيذ الدالة function() # استدعاء الدالة print 'After' return function_decorator # إرجاع الدالة مُزَخْرَفَةً def say_hello(): # إنشاء دالة عادية print 'Hello!' say_hello = decorator(say_hello) # زخرفة الدالة say_hello() # استدعاء النُسخة المُزخرفة من الدالة توفر لنا لغة بايثون طريقة أكثر مرونة لزخرفة الدوال، وهي بوضع اسم المُزخرف مسبوقا بالحرف @ قبل تعريف الدالة. أي أنّ السّطر التالي: def say_hello(): print 'Hello!' say_hello = decorator(say_hello) # زخرفة الدالة يُمكن أن يكون كالتالي: @decorator def say_hello(): # إنشاء دالة عادية print 'Hello!' وبالتالي سنتمكن من زخرفة أي دالة نرغب بزخرفتها كالتالي: @decorator def say_hello(): print 'Hello!' @decorator def say_hi(): print 'Hi!' @decorator def say_name(): print 'Abdelhadi!' say_hello() say_hi() say_name() عند تنفيذ الشيفرة أعلاه ستكون المخرجات كالتالي: Before Hello! After Before Hi! After Before Abdelhadi! After مُلاحظة: إذا كانت للدالة معاملات فما عليك إلا استخدام args* التي سبق وتحدثنا عنها في الدرس السابق. # -*- coding: utf-8 -*- def decorator(function): # إنشاء الدالة المسؤولة عن الزخرفة def function_decorator(*args): # إنشاء الدالة التي ستكون نسخة مزخرفة من الدالة المُمرّرة كمُعامل print 'Before' # طباعة جملة قبل تنفيذ الدالة function(*args) # استدعاء الدالة print 'After' return function_decorator # إرجاع الدالة مُزَخْرَفَةً لاحظ الدالتين function_decorator و function. أمثلة على المزخرفات في لغة بايثون إذا فهمت مبدأ المزخرفات فستستطيع أن تتعامل مع الدوال بمرونة عالية، وإليك بعض الأمثلة لاستخدام المُزخرفات لتأدية بعض المهام البسيطة: حساب مدة تنفيذ دالة إذا فهمت جيدا مبدأ المُزخرفات فستلاحظ بأنّك تستطيع تنفيذ مهام قبل تنفيذ الدالة ومهام بعد تنفيذها، ومما سبق نستنتج بأنّنا نستطيع أن نقوم بحفظ الوقت الحالي في مُتغير ثم تنفيذ الدالة وبعدها نقوم بحساب الفرق بين الوقت السابق والوقت الحالي، ما سيُرجع المُدة المُستغرقة لتنفيذ الدالة. البرنامج التالي مثال على مُزخرف لحساب مُدة دالة تطبع الجملة !Hello World مليون مرّة. # -*- coding: utf-8 -*- import time # جلب مكتبة الوقت لاستعمال دوال الوقت def function_time(function): def function_decorator(*args): start_time = time.time() # الحصول على وقت البداية function(*args) end_time = time.time() # الحصول على الوقت بعد نهاية التنفيذ # طباعة اسم الدالة والفرق بين وقت البداية ووقت النهاية print '%s function took %0.3f s' % (function.func_name, (end_time - start_time)) return function_decorator # إرجاع الدالة مُزَخْرَفَةً # زخرفة الدالة المسؤولة عن الطباعة مليون مرة @function_time def print_million_times(): for i in range(0, 1000000): print 'Hello World! 1,000,000 times!' print_million_times() البرنامج أعلاه سيطبع الجملة مليون مرة ثم يعرض الوقت المُستغرق لإنجاز العملية. الجملة الأخيرة ستكون شيئا كالتّالي: print_million_times function took 69.584 s ملاحظات: نستعمل التابع func_name للحصول على اسم الدالة المُمررة كمعامل، ويكون على شكل سلسلة نصية. نستعمل الجملة time.time للحصول على الوقت بالثواني، عدد الثواني الذي تنتجه الجملة هو عدد الثواني الذي مرّ منذ سنة 1970. يُمكنك استعمال هذا المُزخرف مع أي دالة تريد فقط اكتب اسم المُزخرف مسبوقا بالحرف @ ثم عرف الدالة بعده، وستحصل على الوقت المُستغرق لتنفيذ الدالة. حساب عدد مرات استدعاء دالة يُمكننا أن نستخدم المُزخرفات للحصول على عدد المرات التي استدعيت فيها دالة ما في برنامج مُعيّن، بحيث يحمل متغير قيمة العدد صفر، وفي كل مرة تستدعى فيها الدالة، فإن المُتغير يحمل القيمة مع زيادة بالعدد واحد، انظر المثال التالي. # -*- coding: utf-8 -*- # متغير العد n = 0 # المُزخرف def call_times(function): def decorated(): function() # استدعاء الدالة global n # جعل مُتغير العدّ عالميا n += 1 # زيادة قيمة المُتغير print 'Function was called', n, 'times' # طباعة قيمة المُتغير return decorated @call_times # زخرفة الدالة def func(): # إنشاء الدالة print 'Hello!' # استدعاء الدالة func() func() func() func() مُخرجات البرنامج أعلاه ستكون كالتالي: Hello! Function was called 1 times Hello! Function was called 2 times Hello! Function was called 3 times Hello! Function was called 4 times يُمكنك إصلاح الجمل من الناحية اللغوية بإضافة بعض العبارات الشرطية للبرنامج. إنشاء مزخرف لتنفيذ دالة عند تحقق شرط معين فقط يُمكنك أن تستعمل دالة تسجيل الدخول التي قُمنا بإنشائها كمُزخرف للدوال التي تحتاج لأن يكون المُستخدم مُسجلا دخوله. مثلا لنقل بأنّنا نريد أن نعرض على المُستخدم عدة خيارات بعضها يحتاج إلى تسجيل دخول المُستخدم وبعضها لا. الخيارات كالتّالي: تسجيل مُستخدم جديد (تسجيل الدخول غير مطلوب) طباعة جملة عشر مرات ( تسجيل الدخول غير مطلوب) الحصول على الوقت الحالي ( تسجيل الدخول غير مطلوب) طباعة اسم المُستخدم (تسجيل الدخول مطلوب) رؤية معلومات الحساب (تسجيل الدخول مطلوب) تعديل كلمة المرور (تسجيل الدخول مطلوب) مبدأ عمل البرنامج سيكون كالتالي: إنشاء الدوال المسؤولة عن الخيارات عرض الخيارات على المُستخدم زخرفة الدوال التي تطلب تسجيل المُستخدم بمُزخرف تسجيل الدخول المُزخرف سيتحقق من أنّ المُستخدم قد سجل دخوله، إذا كان الأمر كذلك، تنفّذ الدالة وإذا لم يتحقق الشرط فلا تنفذ. لنقل بأنّ اسم مُزخرف التحقق من تسجيل الدخول هو is_user_logged_in، ستكون الدوال التي تطلب تسجيل الدخول مُزَخْرَفَةً كالتالي: @if_user_logged_in def account_info(): print 'Username:', username, 'Password:', password تمارين تمرين 1 أنشئ دالة للجمع بين عددين، وبعدها أنشئ مُزخرفا يقوم بمُضاعفة النتيجة. تمرين 2 أنشئ دالة للحصول على قيم من المُستخدم وقم بزخرفة لطباعة جملة ترحيب قبل استدعاء الدالة وجملة توديع بعد استدعاءها. تمرين 3 أكمل البرنامج الخاص بالمثال الثالث (إنشاء مُزخرف لتنفيذ دالة عند تحقق شرط مُعين فقط). تفاصيل التمرين موجودة بالمثال.
- 4 تعليقات
-
- 5
-
- decorators
- مزخرفات
- (و 4 أكثر)
-
تعرّفنا إلى الآن على كيفيّة إنشاء تطبيق بسيط بلغة بايثون وإطار العمل Flask، يُمكن للتّطبيق الاتّصال بقاعدة بيانات، إضافة عناصر، عرضها وحذفها، لكنّ هذا التّطبيق لا يعتبر محميّا، إذ يُمكن لأي كان أن يُضيف مقالات أو يحذفها، لذا من المفضّل أن نقوم بإضافة نظام لتسجيل دخول بحيث تتمكّن وحدك (كمُدير للتّطبيق) من إجراء العمليّات الحسّاسة، بنهاية هذا الدّرس سنمتلك تطبيقا محميا باسم للمُستخدم وكلمة مرور، ولن يتمكّن أحد من حذف المقالات أو إضافتها إلّا إذا حصل على معلومات تسجيل الدّخول. مفهوم الجلسة في إطار العمل Flask الجلسة عبارة عن كائن يتصرّف تماما كقاموس يحتوي على مفاتيح وقيم، الجلسة تكون مرتبطة بنافذة المُتصفّح افتراضيّا، ما يعني بأنّ المفتاح والقيمة ستُسجّلان طيلة مدّة فتح المُتصفّح، وبمجرّد إغلاق نافذة المُتصفّح فإنّ الجلسة تُحذف (أو تدمّر). فكرة تسجيل الدّخول والخروج في درسنا ستكون كالتّالي: سنضع نموذج HTML في أعلى الصّفحة الرّئيسيّة، سيحتوي النّموذج على حقلين، حقل لكتابة اسم المُستخدم، وآخر لكلمة المرور مع زرّ لإرسال طلب الدّخول (أو طلب الاستيثاق Authentication)، عندما يُدخل المُستخدم كلمة "admin" في حقل اسم المُستخدم، وكلمة "password" في حقل كلمة المرور (يُمكن تغيير هذه المعلومات ببساطة)، سيتأكّد التّطبيق من أنّ البيانات صحيحة، وإذا كانت صحيحة فسنستخدم القاموس session المتواجد في حزمة flask لإنشاء مفتاح باسم logged_in وسنضع القيمة المنطقيّة True للمفتاح، بعدها يُمكننا أن نوجّه المُستخدم إلى الصّفحة الرّئيسيّة مع إخفاء نموذج تسجيل الدخول وعرض رابط لتسجيل الخروج عوضا عن النّموذج، أمّا إذا أدخل المُستخدم بيانات استيثاق خاطئة فسنوجّهه إلى الصّفحة الرّئيسيّة مع عرض نموذج تسجيل الدّخول مُجدّدا. تسجيل الدخول سنقوم أولا بإضافة نموذج HTML لتمكين الزّائر من إدخال بيانات الاستيثاق، سنضيف الشيفرة التّالية في ملفّ index.html مُباشرة بعد وسم body: <form action="{{ url_for('login') }}" method='POST'> <input type="text" placeholder="اسم المُستخدم" name="username"> <input type="password" placeholder="كلمة المرور" name="password"> <input type="submit" value="اُدخُل"> </form> الشيفرة أعلاه عبارة عن نموذج لحقلي اسم المُستخدم وكلمة المرور، سنتمكّن من الحصول على القيمتين في ملفّ app.py كالآتي: username = request.form['username'] password = request.form['password'] لاحظ بأنّنا حدّدنا مُوجّها باسم login في النّموذج، لذا فسيتوجّب علينا إنشاؤه في ملفّ app.py والأمر شبيه بما فعلناه في الدّرس السّابق، بحيث يقبل المُوجه طريقة POST لاستقبال البيانات التي يُرسلها المُتصفّح. سيكون موجّه login كالتّالي: # Login Route @app.route("/login", methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] if username == "admin" and password == "password": session['logged_in'] = True else: return redirect(url_for('home')) return redirect(url_for('home')) يستقبل الموجّه اسم المُستخدم وكلمة المرور ويُنفّذ جملة شرطيّة للتحقق من أنّ البيانات صحيحة، لنفرض بأنّ اسم المُستخدم هو admin وكلمة مروره هي password. إذا تحقّق الشّرط وتأكّدنا من أن البيانات التّي أدخلها المُستخدم صحيحة نقوم بإنشاء جلسة باسم logged_in ونعطيها القيمة المنطقيّة True ونقوم بعد ذلك بتوجيه المُستخدم إلى الصّفحة الرّئيسيّة، أمّا إن لم تكن البيانات صحيحة فنقوم بتوجيه المُستخدم إلى الصّفحة الرّئيسيّة دون إنشاء جلسة. لاستخدام الجلسات سيتوجّب علينا استيرادها من Flask في بداية الملفّ، ليُصبح السّطر كالتّالي: from flask import Flask, render_template, redirect, url_for, request, session سيتطلّب استخدام الجلسات تخصيص مفتاح سريّ كذلك، ويُمكن أن نقوم بذلك بإضافة السّطر التّالي مُباشرة بعد تعريف المُتغيّر app. app = Flask(__name__) app.config['SECRET_KEY'] = "Secret" تنبيه: من المهم أن تُغيّر Secret إلى مفتاح لا يُمكن التّنبؤ به ويجب أن يكون سريّا للغاية، يُمكنك استخدام دالة urandom من الوحدة os لتوليد سلسلة عشوائيّا كالتّالي: >>> import os >>> os.urandom(24) '\xee\x9dA\x81\x19\x17\xdd\x04\xae9\xc1\x1a-\xf2\xf8\xda\x9a\x99u\x90\x96]\xbaT' يُمكنك بعد ذلك استعمال السّلسلة المولّدة كقيمة للمفتاح السرّي. وهكذا سنكون قد انتهينا من نظام تسجيل دخول المُدير، الخطوة التّالية هي تسجيل خروجه، وذلك بتدمير الجلسة عند الوصول إلى المُوجّه logout. تسجيل الخروج لتسجيل الخروج يكفي إضافة مُوجّه باسم logout إلى الملفّ app.py، وسيكون دور الموجّه حذف المفتاح logged_in من الجلسة: # Logout Route @app.route("/logout") def logout(): session.pop('logged_in', None) return redirect(url_for('home')) لاحظ بأنّ الموجّه بسيط للغاية، كلّ ما يقوم به هو حذف المفتاح logged_in باستخدام التّابع pop وبعدها يعيد توجيه المُستخدم إلى الصّفحة الرّئيسيّة. الآن إذا قمت بتسجيل دخولك فسيُنشئ التّطبيق جلسة جديدة، أما إذا قمت بزيارة الموجّه logout عبر العنوان http://127.0.0.1:5000/logout فستُدمّر الجلسة. إضافة زر لتسجيل الخروج عوضا عن نموذج HTML رغم أنّنا قُمنا بإضافة نظام لتسجيل الدّخول والخروج إلا أنّ ذلك لا يظهر في الصّفحة، ولا يُمكن لنا أن نعرف تسجيل الدّخول من عدمه. سنقوم في هذا الجزء بتحويل الصّفحة الرّئيسيّة إلى صفحة مُتجاوبة، أي أنّنا سنتأكّد ممّا إذا كان المُستخدم قد سجّل دخوله، فإن كان ذلك صحيحا فسنعرض له زرّا لتسجيل الخروج، أمّا إن لم يكن قد سجّل دخوله فسنعرض نموذج HTML لتسجيل الدّخول. يُمكن القيام بالأمر بإضافة شرط للتأكّد من أنّ المفتاح logged_in موجود في الكائن session، والتّالي تطبيق للأمر في ملفّ index.html: {% if 'logged_in' not in session %} <form action="{{ url_for('login') }}" method='POST'> <input type="text" placeholder="اسم المُستخدم" name="username"> <input type="password" placeholder="كلمة المرور" name="password"> <input type="submit" value="اُدخُل"> </form> {% else %} <a class="logout" href="{{ url_for('logout') }}">خروج</a> {% endif %} يُمكن الآن التمييز بين تسجيل الدخول وتسجيل الخروج؛ الخطوة التّالية هي حماية الموجّهين create و delete لنمنع من لم يسجّل دخوله من حذف وإنشاء المقالات والسّماح بذلك للمُدير فقط. حماية الموجهين create و delete لحماية موجّه ما يجب أن نحمي الدوال التي التّي تقوم بالعمليّة، ما يعني بأنّنا يجب أن نحمي الدّالتين create و delete. وللقيام بالأمر يُمكن الاستعانة بميّزة المُزخرفات في لغة بايثون، يُمكنك الحصول على المزيد من المعلومات بالرّجوع إلى درس المُزخرفات. . سننشئ مُزخرفا باسم login_required، سنحتاج إلى المُزخرف wraps من الوحدة functools وهو مُزخرف مُساعد ويعتبر استعماله من أفضل المُمارسات. سيكون المُزخرف login_required كالتّالي: from functools import wraps def login_required(function): @wraps(function) def wrapper(*args, **kwargs): if 'logged_in' in session: return function(*args, **kwargs) else: return redirect(url_for('home')) return wrapper نقوم أولا باستدعاء wraps من الوحدة functools ونزخرف الدّالة بشرط أن يكون المفتاح logged_in في الجلسة، ما يعني بأنّ الدّالة ستُنفّذ إذا كان المُستخدم قد سجّل دخوله فقط، أمّا إن لم يكن قد سجّل دخوله فسنقوم بتوجيهه إلى الصّفحة الرّئيسيّة دون تنفيذ الدّالة. يُمكن الآن تطبيق المُزخرف على الدّالتين create و delete لمنع الزوار من إنشاء المقالات أو حذفها. لتطبيق المُزخرف يكفي كتابة اسمه مسبوقا بالرّمز @ مُباشرة فوق الدّالة. ما يعني بأنّ الموجّه create سيصبح كالتالي: # Create Post Page @app.route("/create", methods=['GET', 'POST']) @login_required def create(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] manage_db.create(title, content) return redirect(url_for('home')) أما موجّه delete فسيصبح كالتّالي: # Delete Post @app.route("/delete/<post_id>") @login_required def delete(post_id): manage_db.delete(post_id) return redirect(url_for('home')) يمكنك تصفّح ملفّات الأمثلة على Github. ختاما إلى هنا نكون قد تعرّفنا على طرق بسيطة لحماية تطبيقنا ويُمكنك الآن نشر التّطبيق على الأنترنت، لكن تذكر بأنّ هذه الطّرق ليست آمنة بما فيه الكفاية، لذا إن كان تطبيقك حسّاسا فمن المُفضّل استعمال إضافة لإدارة أنظمة الاستيثاق مثل إضافة Flask-login أو Flask-Security.