اذهب إلى المحتوى

عبدالهادي الديوري

الأعضاء
  • المساهمات

    368
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    22

كل منشورات العضو عبدالهادي الديوري

  1. أين المُتحكّمان HomeCtrl و ResumeCtrl أعتقد بأنّ هذا ما ينقصك. أضف المُتحكمات إلى تطبيقك. app.controller('HomeCtrl',function($scope){ }); app.controller('ResumeCtrl',function($scope){ }); ضعها تحت الـRoute
  2. ما فهمته هو أنّك ترغب بإضافة الأغراض إلى سلّة المُشتريات دون اللجوء إلى قاعدة بيانات، وحفظها فقط أثناء الجلسة الحالية (بحيث تخزّن البيانات في جهاز المُستخدم دون قاعدة بيانات تطبيقك). يُمكن القيام بذلك بتخزين بيانات المُشتريات في الكعكات Cookies أو في خاصيّة localStorage في HTML5.
  3. وعليكم السّلام ورحمة الله وبركاته. أعتقد بأنّك تقصد AngularJS. أليس كذلك؟ هلّا عدّلت سؤالك مع تضمين الشّيفرة المسؤولة عن المُشكلة لو سمحت؟ قد تكون المُشكلة في عدم تحديد مُتحكّم Controller للموجّه route.
  4. سنتحدثُ في هذا الدّرس عن الطرق التي يُوفّرها لك كلّ من Bash، نظام لينكس والطرفيّة Terminal للتّحكم بالوظائف والعمليّات. ذكرنا في درس إدارة العمليات في لينكس باستخدام الطرفية كيفية استعمال الأوامر ps ،kill و nice للتحكم بالمهام على نظامك. سنُركّز في هذا المقال على إدارة عمليّات المُقدّمة وعمليّات الخلفيّة وسنشرح كيف ترفع من مُستواك في التّحكم بالوظائف والمهام لتحصل على مُرونة أكثر عند تنفيذك للأوامر. إدارة عمليات المقدمة مُعظمُ العمليّات التي تُشغّلها على لينكس ستُنفّذ في المُقدّمة. سيبدأ الأمر بالتّنفيذ ما يمنعك من تنفيذ أي أمر آخر طوال مدة تنفيذ العمليّة. يُمكن أن تسمح العمليّة بتَفاعلِ المُستخدم، أو يُمكن أن تُنفّذ إجراءًا معيّنا ثم تنتهي. وسيُعرض أي مُستخرج على شاشة الطرفية افتراضيّا، سنتطرّق إلى الطريقة المُبسّطة لإدارة عمليّات المُقدّمة أسفله. تشغيل عملية تُشَغّلُ العمليّات في المُقدّمة افتراضيا إلى أن تنتهي أو عند تغيّر الحالة، وأثناء تشغيل العمليّات لن تتمكن من التفاعل مع الصّدفة Shell. بعض أوامر المُقدّمة تنتهي بسرعة مُرجعة المُستخدم إلى واجهة الصدفة آنيّاً، وهذا الأمر هو مثال على ذلك: echo "Hello World" هذا الأمر سيطبع "مرحبا بالعالم" على شاشة الطرفيّة ثم يرجعك إلى شاشة الأوامر. بعض أوامر المُقدمة الأخرى تأخذ وقتا أطول للتنفيذ، مانعة الولوج إلى الصّدفة أثناء التشغيل. يُمكن أن يعود السبب إلى أن الأمر يُشغل عمليّة أعقد أو أنّ العمليّة مُبرمجة للتنفيذ إلى أن تتوقف بشكل واضح أو إلى أن تتلقى مُدخلات من المُستخدم. تُعتبر أداة top من الأوامر التي تنفذ لوقت غير محدود، فبعد تشغيلها ستستمر في التنفيذ وتحديث عناصر الشاشة إلى أن يقوم المُستخدم بإنهاء العملية: top يُمكنك الخروج بالضّغط على "q". بعض العمليّات لا تملك طريقة للخروج ولتوقفها سيجب عليك استخدام طريقة أخرى. إنهاء عملية لنفترض أننا قمنا بتشغيل حلقة تكرار بسيطة في سطر الأوامر، يُمكننا تشغيل حلقة تكرار تقوم بطباعة "Hello World" كل عشر ثوان. هذه الحلقة ستستمر في الطّباعة إلى أن تُوقَف إجباريّاً: while true; do echo "Hello World"; sleep 10; done حلقات التكرار كهذه لا تملك أي زر للإلغاء، سيتوجّب علينا أن نوقف العمليّة بارسال ما يسمّى بـ الإشارة signal. تستطيع النواة في لينكس إرسال إشارات إلى العمليّات لطلب إيقاف نهائيّ للعملية أو تغيير الحالة فقط، طرفيّات لينكس عادة ما تكون مضبوطة لإرسال إشارة "SIGNT" (الإشارة رقم 2) لعمليّة المُقدّمة الحاليّة عند ضغط تركيبة المفتاحين CTRL+C. تُخبر إشارة "SIGNT" البرنامج بأن المستخدم قد طلب إنهاء العمليّة مُستخدما لوحة المفاتيح. لإيقاف حلقة التكرار التي بدأناها اضغط على مفتاحي CTRL و"c": CTRL+C سيتم إغلاق حلقة التكرار و ستتمكن من التحكم بالصّدفة Shell مجددا. إشارة "SIGNT" المُرسلة عن طريق تركيبة CTRL+C واحدة من الإشارات المتعدّدة التي يمكن إرسالها إلى البرامج، مُعظم الإشارات لا تملك تركيبة مفاتيح مُرتبطة بها، وعليك إرسال هذه الإشارات باستخدام الأمر kill عوضا عن ذلك (سوف نغطي ذلك لاحقاً). تعليق العمليات ذكرنا أعلاه أن عمليّات المُقدّمة تمنعك من تنفيذ أي أمر آخر طوال مدة تنفيذ العمليّة. ماذا لو أدركنا أنّنا نحتاج إلى شاشة الطرفية بعد أن نُشغّل عملية في المُقدمة؟ إشارة "SIGTSTP" (الإشارة رقم 20) هي إشارة من الإشارات التي يمكن إرسالها إلى عمليّات المُقدّمة. عندما نضغط تركيبة مفاتيح CTRL+Z، تُسجل الطرفيّة أمر "تعليق"، الذي يرسل إشارة "SIGTSTP" إلى عملية المُقدمة. هذا الأمر سيوقف مؤقّتا تنفيذ الأمر ويعيد التحكم بالطرفيّة. كمثال على ذلك، لنستخدم أمر ping للاتّصال ب google.com كلّ 5 ثوان. سوف نُسبق الأمر بكلمة command، ما يُخولُ لنا تجاوز أي كُنيات للصدفة (shell aliases) يُمكن لها وضع عدّ أقصى على الأمر : command ping -i 5 google.com عوضا عن إنهاء العملية باستعمال CTRL+C، أدخل CTRL+Z بدلا من ذلك. CTRL+Z سيكون المُخرج هكذا: [1]+ Stopped ping -i 5 google.com لقد تم إيقاف الأمر ping مؤقّتاً، وبذلك تستطيع الوصول إلى جلسة الصّدفة shell لكتابة الأوامر مُجدّداً. يُمكننا استعمال أداة إدارة العمليّات ps لعرضها: ps T PID TTY STAT TIME COMMAND 26904 pts/3 Ss 0:00 /bin/bash 29633 pts/3 T 0:00 ping -i 5 google.com 29643 pts/3 R+ 0:00 ps t يُمكننا ملاحظة أن عمليّة ping لا تزال ضمن القائمة، لكن العمود STAT (الخاص بحالة العمليّة) يحمل القيمة "T". صفحة الدليل الخاصة بـ ps تخبرنا أن هذا الحرف يعني أن العملية قد أوقفت عن طريق إشارة "التحكم بالعمليات". سوف نتحدّث بعمق أكثر عن كيفية تغيير حالة العمليّة،لكن في الوقت الحالي يُمكننا استئناف تنفيذ العمليّة في المُقدمة بكتابة: fg عند استكمال التنفيذ، أنهِ العمليّة باستعمال CTRL+C: CTRL+C إدارة عمليات الخلفية البديل الرئيسي لتنفيذ عمليّة في المُقدمة هو تشغيلها في الخلفيّة، عمليّة الخلفية مرتبطة بالطرفيّة التي شغّلتها منها، لكنّها لا تمنعك من الوصول إلى الصّدفة (سطر الأوامر)، عوضا عن ذلك، تُنفّذ العمليّة في الخلفيّة، تاركة لمُستخدم حريّة التفاعل مع النظام أثناء تشغيل العمليّة. بسبب الطريقة التي تتعامل بها عمليّات المقدمة مع الطرفيّة، يمكن أن تُشغّل عمليّة واحدة فقط لكل نافذة من نوافذ الطرفية. ولأن عمليّات الخلفية ترجع التحكم بالصّدفة Shell مباشرة بدون انتظار انتهاء العمليّة، يُمكن تشغيل العديد من العمليّات في الخلفيّة في الآن ذاته. تشغيل العمليات يُمكنك تشغيل عمليّة في المُقدّمة بإلحاق محرف"&" عند نهاية الأمر. يُخبر هذا الصّدفة shell بألّا تنتظر إلى حين انتهاء العملية بل تشغيلها وإرجاع التحكم بالطرفيّة للمُستخدم فوراً. ستبقى المُخرجات تُعرض على شاشة الطرفية (إلا إذا قمت بـإعادة توجيهها) لكنّك ستتمكن من كتابة أوامر إضافية أثناء استمرار تنفيذ عمليّة الخلفيّة. على سبيل المثال، يُمكن أن نبدأ أمر ping نفسه من القسم السّابق في الخلفيّة: command ping -i 5 google.com & سترى مخرجا يبدو هكذا من نظام bash للتحكم بالعمليّات : [1] 4287 وسترى أيضا المُخرج العادي من طرف الأمر ping: PING google.com (74.125.226.71) 56(84) bytes of data. 64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=1 ttl=55 time=12.3 ms 64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=2 ttl=55 time=11.1 ms 64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=3 ttl=55 time=9.98 ms رغم هذا ستتمكن من كتابة الأوامر في الوقت عينه. سوف يُمزج المُخرج من عمليّة الخلفيّة مع المدخلات والمخرجات الخاصة بعمليّة المُقدمة التي بدأتها، لكنّها لن تتداخل مع تنفيذ عمليّة المقدّمة. جدولة عمليات الخلفية لرؤية جميع العمليّات المُتوقفة أو عمليّات الخلفية، يمكنك استعمال الأمر jobs: jobs إذا كان الأمر ping مُشغّلا في الخلفية ستكون المخرجات على الشكل التالي: [1]+ Running command ping -i 5 google.com & يُبيّن هذا أنّنا نملك عمليّة واحدة في الخلفيّة. [1] تمثّل الـ "job spec" أو رقم الوظيفة. يُمكننا الإشارة إلى هذه العمليّة عند استعمال أوامر التحكم بالعمليّات مثل kill ،fg ،bg بإضافة سابقة "%" إلى رقم الوظيفة. في هذه الحالة سنشير إلى هذه الوظيفة بالإشارة 1%. إيقاف عمليات الخلفية يُمكننا إيقاف عمليّات الخلفيّة بعدّة طرق، أكثر طريقة نجاعة هي استخدام الأمر kill مع رقم الوظيفة. كمثال، يمكن أن نوقف أمرنا المُشغَّل في الخلفيّة بكتابة: kill %1 حسب طريقة ضبط الطرفيّة التي تستخدمها، إما مُباشرة أو بعض ضغط ENTER للمرة الثانية، ستشاهدُ حالة إيقاف الوظيفة على الطرفيّة: [1]+ Terminated command ping -i 5 google.com إذا تحققنا بالأمر jobs، سنلاحظ عدم وجود أي عمليّة. تغيير حالة العملية بعد أن تعرفنا على كيفية تشغيل عمليّة أو إيقافها في الخلفيّة، يُمكننا التحدث عن كيفية تغيير حالة العمليّة. شرحنا تغيير حالة واحدة قبل قليل عند التطرق لكيفيّة إيقاف مُهمّة أو تعليق عملها باستخدام CTRL+Z. يُمكننا نقل عمليّات المقدّمة إلى الخلفيّة أو العكس عندما تكون العمليّة في حالة توقّف. نقل عمليات المقدمة إلى الخلفية إذا نسيت أن تنهي الأمر بحرف العطف "&"، لا تزال قادرا على نقل هذه العمليّة إلى الخلفيّة. الخطوة الأولى هي إيقاف العمليّة باستخدام CTRL+Z: CTRL+Z عندما تتوقف العمليّة، يُمكننا استعمال الأمر bg لتشغيلها مجدّدا في الخلفيّة: bg ستشاهد حالة الوظيفة مع حرف العطف "&" ملحقا في الأخير: [1]+ ping -i 5 google.com & يعمل الأمر bg افتراضيّا مع آخر عمليّة تمت إيقافها، إذا أوقفت العديد من العمليّات بشكل متتالٍ بدون تشغيلها مجدّدا يُمكنك الإشارة إلى العمليّة برقم الوظيفة لإرسال العمليّة الصحيحة إلى الخلفيّة. لاحظ أنّه ليست كل العمليّات قابلة للإرسال إلى الخلفيّة. ستتوقف بعض العمليّات آليّاً إذا كشفت أنها نُفّذت مع إدخال ومخرج معياري متصلة مُباشرة مع طرفيّة نشطة. نقل عمليات الخلفية إلى المقدمة يُمكننا أيضاً نقل عمليّات الخلفيّة إلى المُقدّمة باستخدام الأمر fg: fg هذا الأمر يعمل مع آخر عمليّة منقولة إلى الخلفيّة، يُشار إليها بعلامة "+" في مخرجات jobs. الأمر يقوم فوراً بتعليق العمليّة ووضعها في المُقدّمة. لتحديد وظيفة أخرى استعمل رقم الوظيفة: fg %2 عندما تُنقل الوظيفة إلى المُقدّمة يُمكنك إمّا إنهاؤها مع CTRL+C، أو تركها لتكتمل أو تعليقها وإرسالها إلى الخلفيّة من جديد. التعامل مع إشارات SIGHUP سواء كانت العمليّة في المقدّمة أو في الخلفيّة، فستبقى مرتبطة بالطرفيّة التي بدأت العمليّة منها، عندما تغلق الطرفيّة فإنها ترسل إشارة SIGHUP لجميع العمليّات (في المُقدمة، في الخلفيّة، أو المتوقّفة) المرتبطة بها. ما يُشير إلى العمليّات بالإنهاء لأن الطرفيّة المُتحكِّمة ستصبح غير مُتوفرة قريباً، ماذا لو أردت إغلاق طرفيّة مع إبقاء تنفيذ عمليّات الخلفيّة؟ هناك العديد من الطرق للقيّام بذلك، أكثر الطرق مرونة هي باستعمال مُضاعف طرفيّة (terminal multiplexer) مثل tmux أو screen، أو استعمل أداة تمنح خاصية الفصل مثل dtach. على الرغم من هذا فهذه الطرق ليست دائما خيّارا ممكنا، ففي بعض الأوقات قد لا تكون هذه البرمجيات متوفرة أو أنّك قد سبق وبدأت العمليّة التي تريدها أن تستمر في التنفيذ بعد إغلاق الطرفيّة. استعمال nohup إذا كنت تعلم أنك عند تشغيل العمليّة سترغب في إغلاق الطرفيّة قبل انتهاء العمليّة، يُمكنك تنفيذها باستخدام أمر nohup، هذا الأمر يؤمّن العمليّة من التعرض إلى الإنهاء من طرف إشارة SIGHUP وستستمر في التنفيذ عندما تُغلق الطرفيّة. وستسجل كابن للنظام الأساسي: nohup ping -i 5 google.com & سترى سطرا يبدو كالتالي، مبيّنا أن مُخرجات الأمر ستُكتب في ملف يسمى nohup.out (في المجلّد الحالي إذا ما كان قابلا للكتابة عليه، في حالة لم يكن قابلا للكتابة عليه فسيُنشئه في مُجلّد المنزل): nohup: ignoring input and appending output to 'nohup.out' والغرض منه التأكد من أن المخرجات ليست مفقودة بعد إغلاق الطرفيّة. إذا أغلقت نافذة الطرفية وفتحت أخرى، ستبقى العمليّة في طور التنفيذ، ولن تتمكن من رؤيتها في مخرجات الأمر jobs لأن كل طرفيّة تحمل صفّ وظائف خاص بها، إغلاق الطرفيّة سبّب تدمير الوظيفة ping رغم أن العمليّة ping لا تزال تُنفّذ. لإيقاف عمليّة ping، يجب عليك البحث عن معرّف العمليّة (process ID) أو PID الخاص بها، يُمكنك القيّام بذلك بالاستعانة بالأمر pggrep (هناك أيضا الأمر pkill، لكنّنا بهذه الطريقة المقسّمة إلى خطوتين نكون متأكدين من أننّا سنوقف العمليّة التي نريدها فقط). استعمل pgrep مع a- للبحث عن العمليّة: pgrep -a ping المُخرجات: 7360 ping -i 5 google.com يُمكنك بعدها إيقاف العمليّة بإلحاق معرف العمليّة (الرقم في العمود الأول) بالأمر kill: kill 7360 قد ترغب في حذف ملف nohup.out إذا لم تكن تريده بعد الآن. استعمال disown يكون الأمر nohup مُفيدا فقط عندما تعلم أنّك ستحتاج إليه عند بدأ عمليّة ما. نظام إدارة الوظائف الخاص بـ bash يتيح لنا طرقا أخرى للحصول على نتائج مماثلة مع أمر diswon. عند الضبط الافتراضي للأمر disown، يقوم الأمر بحذف الوظيفة من طابور الوظائف الخاص بالطرفيّة. ما يعني أنك لن تستطيع إدارة العمليّة باستعمال التقنيّات التي تطرقنا إليها (مثل fg ،bg ،CTRL+Z ،CTRL+C). سيُحذف فوريّا من قائمة مخرجات الأمر jobs ولن يكون مرتبطا بالطرفيّة بعد ذلك. يُستعمل الأمر عن طريق تحديد العمليّة بالاستعانة برقم وظيفتها، على سبيل المثال، لتتبرر (disown) من الوظيفة رقم 2، يمكننا كتابة: disown %2 هذا الأمر سيترك الوظيفة في حالة مثل الأمر nohup، الاستثناء الوحيد هو أن جميع المُخرجات ستُفقد بعد إغلاق الطرفيّة في حالة لم يتم إعادة توجيهها إلى ملف ما. لن ترغب عادة في حذف العمليّة من قائمة التحكم بالوظائف إذا لم تكن تريد إغلاق نافذة الطرفيّة فورا.يمكنك إضافة h- للأمر لتقوم العمليّة بتجاهل إشارات SIGHUP، وتستمرَّ في العمل كوظيفة عاديّة: disown -h %1 في هذه الحالة، يُمكنك استعمال تقنيّات التحكم بالوظائف للاستمرار في التحكم بالوظيفة إلى حين إغلاق الطرفيّة، عند إغلاق الطرفيّة، ستكون عالقا -مرة أخرى- مع عمليّة دون أي مُخرجات إذا لم تعِد توجيهها إلى ملف عندما بدأتها. للعمل على ذلك، يمكنك محاولة إعادة توجيه مخرجات العمليّة بعد أن شغلتها. هذا الأمر خارج مجال هذا الدرس. يمكنك الإطلاع على الدرس مقدمة إلى إعادة توجيه الإدخال/الإخراج (I/O) في لينكس لمزيد من المعلومات. استعمال خيار الصدفة: huponexit هناك أيضا طريقة أخرى في Bash لتجنّب مشكلة إشارات SIGHUP للعمليّة الابن، يتحكم خيّار huponexit في ما إذا كان bash سيرسل إشارة SIGHUP للعمليّة الابن عند الخروج أو لا. ملاحظة: خيّار huponexit يؤثّر على طريقة تصرف إشارات SIGHUP فقط إذا كان إغلاق جلسة الصّدفة من داخل الصّدفة نفسه. يعتبر الأمر exit أو CTRL+D من الأمثلة على إغلاق الجلسة من داخل الصّدفة. عند إنهاء جلسة صدفة من برنامج الطرفيّة (بإغلاق النافذة، ...)، لن يكون للأمر huponexit أي تأثير، عوضا عن ترك اتخاذ قرار إرسال إشارة SIGHUP لـ bash سترسل الطرفيّة بنفسها الإشارة إلى bash، والتي ستبثّ (بشكل صحيح) الإشارة إلى العمليّة الابن. رغم المحاذير أعلاه، يعتبر خيار huponexit أسهل من الطرق. يُمكنك معرفة ما إذا كانت هذه الخاصية مُشغّلة أو لا بكتابة: shopt huponexit لتشغيل الخاصيّة: shopt -s huponexit الآن، إذا قمت بالخروج من الجلسة بالأمر exit، فإن العمليّات ستستمر في العمل: exit لدى هذا الأمر نفس المحاذير حول المُخرجات مع الخيّار السابق، لذلك تأكد أنّك قمت بتوجيه مُخرجات العمليّة قبل إغلاق الطرفيّة إذا كانت مُهمّة. خاتمة سيمنحك تعلّم التحكم بالوظائف وكيفيّة إدارة عمليّات المُقدمة وعمليّات الخلفيّة مرونة أكثر عند تشغيل البرامج من سطر الأوامر. عوضا عن فتح عدّة طرفيّات أو جلسات SSH، تستطيع أن تدبّر الأمر ببضع أوامر إيقاف وإرسال إلى الخلفيّة. ترجمة -وبتصرّف- للمقال How To Use Bash's Job Control to Manage Foreground and Background Processes لصاحبه Justin Ellingwood.
  5. وعليكم السّلام ورحمة الله وبركاته. لا أعلم كثيرا حول هذا النّوع من الخوادم، لكن يبدو لي بأنّها خطّة جيّدة مبدئيّا للحماية. أعتقد بأنّه من الأفضل استخدام خدمات مثل CloudFlare (إن لم تكن تستخدمها مُسبقا) كإجراء احتياطي لحمايته من الهجمات. لكن عليك أن تعلم بأنّ الحماية نسبيّة، ولا يُمكن التأكّد من أنّ الخادم محمي بشكل كامل، لذا من المُستحبّ أن تستشير خبيرا في الحماية.
  6. رأس المال مُهم، لكنّه ليس كلّ شيء، فطريقة التّنفيذ أهم بكثير من رأس المال. حتى ولو كنت تملك رأس مال، فهذا لا يعني أنّك ستُحسن التّعامل معه. رأس المال الكبير والفكرة العظيمة لا يساويان شيئا إن لم تفهم أساسيّات وقواعد ريادة الأعمال، عليك دراسة السّوق، ودراسة من سبقك إلى هذا المجال، ادرس كل شيء من جميع النّواحي، كن زبونا لهم وتعلّم من أخطائهم وركّز على الأمور التّي نجحوا فيها واسأل أسئلة من قبيل (لماذا نجحت هذه الفكرة؟ وكيف؟). كما أنّ البحث عن شريك مؤسّس ليس بتلك السّهولة، إذ عليك أن تتفاعل مع من يهتمون بنفس مجالك، وعليك أن تبرهن لهم بأنّك جيّد كفاية. نصيحتي أن تدرس وتتعلّم وتتقن المجال الذي تهتم به قبل التّفكير في ريادة الأعمال.
  7. هناك تفاوت كبير في تعريف المقالة النّاجحة، قد يعتبرها البعض مقالة خاليّة من شتى أنواع الأخطاء، وقد يعتبرها آخرون مقالة مقدّمة بأسلوب واضح وسلس يجلب القارئ، كل هذه تعريفات مُناسبة لكنّها ليست واجبة بالضرورة. المهم أن توصل رسالتك إلى القارئ. هناك فرق كبير بين أنواع المقالات، فهناك المقال التقني والأدبي والعلمي وغير ذلك من الأنواع، ولا يصح أبدا استخدام نفس الأسلوب في كل نوع، فلكل مجال جمهور مُختلف ذو خلفيّات مُتباينة. أفضل طريقة لمعرفة كيفيّة استخدام الأسلوب الصّحيح هي بالاطلاع على مقالات من سبقك من ذوي الخبرة في نفس مجالك، إذا كنت مهتما مثلا بريادة الأعمال فاقرأ المقالات المتواجدة في قسم ريادة الأعمال في هذا الموقع، حلّل أسلوب الإلقاء وقارنه بأعمالك، واسع إلى تطوير أسلوبك ما استطعت.
  8. بالإضافة إلى ما ذُكر في الإجابتين السّابقتين، أدعوك للاطلاع على هاتين المُشاركتين، فقد تُفيدانك لمعرفة المزيد حول التّسويق المجاني.
  9. للحصول على إجابات أدق، من المُفضّل توفير معلومات حول نوع التّطبيق الذي تبنيه، ماهي التّقنيات التي تستعملها في تطويره؟ مثلا لو كنت تستخدم لغة php وإطار Laravel فمن الأفضل أن يكون السؤال كالتّالي: كيف أضيف خاصيّة الدفع في تطبيق مبني بـ Laravel؟ سؤالك هذا عام، ولا يمكنني الإجابة بإجابة مناسبة حتى تُخبرني عن التقنيات التي تستعملها. ولكنّني سأجيب جوابا عاما وهو أنّك تستطيع استخدام الواجهة البرمجيّة الخاصّة بمشروع Stripe للدّفع الإلكتروني. وتستطيع كذلك استخدام الواجهة البرمجيّة المُتاحة لدى Paypal لنفس الغرض. يُمكنك البحث عن مكتبة مُساعدة حسب اللغة التي تُطوّر بها تطبيقك.
  10. يُمكنك مُقارنة البرمجة مع أي فنّ آخر مثل الطّبخ والهندسة والصّيد والقيادة والرّياضيّات والفيزياء وغير ذلك من الفنون، فهل تحتاج هذه الفنون إلى مُستوى دراسي مُحدد؟ الفرق هنا هو أن البرمجة تُمكّنك من التّعامل مع جهاز الحاسوب وإعطاءه أوامر منطقيّة ليُنفّذها، فإن لم تكن منطقيّة وصحيحة فلن يُنفّذها، تماما كمُحاولة تشغيل السّيارة دون وقود. الأساس هو أنّك تستطيع تعلّم البرمجة كما تعلّمت العلوم التي تُتقنها حاليا، ولا يلزمك لذلك سوى التوكل على الله والصبر والمُثابرة وعدم اليأس. وليس من الضّروري أن تمتلك مستوى دراسيّا مُتقدّما على الإطلاق. وبنفس المنطق، لا يوجد عمر محدد للتعلم، حتى ولو كنت في الخمسينات من عمرك، لا يجب عليك أن تُعطّل عقلك، ويجب عليك استغلاله فيما يُفيدك. أي بتعلّم علم ينتفع به وتعليمه إن استطعت. وكما قال الأستاذ حسن بُرهان، ابدأ بتعلّم لغة بايثون فهي الأنسب للمُبتدئين وإليك هذه السّلسلة من الدروس التي ستفيدك في مشوارك: سلسلة دروس تعلم لغة بايثون وفقك الله
  11. بعد أن تعرّفنا في الدّرس السابق على أساسيات البرمجة كائنية التوجّه، سنُكمل في هذا الجزء ما بدأناه وسنتعلّم بعض المبادئ المتقدّمة حول البرمجة كائنيّة التّوجه. تغيير قيمة متغير الصنف في الدّرس السّابق تعلّمنا كيفيّة تعريف مُتغيّر داخل صنف وكيفيّة الوصول إليه، في هذا الجزء من هذا الدّرس سنتعرّف على كيفيّة استخدام هذه المُتغيّرات بشكل أكثر واقعيّة، لنقل مثلا بأنّنا نمتلك صنفًا باسم Car (سيارة)، ونريد أن ننشئ كائنات من هذا الصّنف، بحيثُ يكون لكل كائن قيم مُختلفة لمُتغيّرات الصّنف، انظر ما يلي: class Car: name = None speed = 0 brand = None def specs(self): print '{} speed is {}Km/h and it\'s brand is {}'.format(self.name, self.speed, self.brand) أنشأنا أعلاه صنفا بسيطا باسم Car مع ثلاثة مُتغيّرات name لاسم السّيارة، speed لسرعة السّيارة و brand لاسم العلامة التّجاريّة، كلمة None تُشير إلى أنّ المُتغيّر مُعرّف لكنّه لا يحمل أية قيمة. بعدها عرّفنا تابعا specs (اختصار لـ specifications) لطباعة مواصفات السّيارة. لننشئ كائنات مُتعدّدة من الصّنف الذّي كتبناه للتو. # Toyota Corolla corolla = Car() corolla.name = 'Toyota Corolla' corolla.speed = 200 corolla.brand = 'Toyota' # Ford Focus focus = Car() focus.name = 'Ford Focus' focus.speed = 220 focus.brand = 'Ford' # Honda Civic civic = Car() civic.name = 'Honda Civic' civic.speed = 210 civic.brand = 'Honda' بعد أن أنشأنا الصّنف والكائنات المندرجة تحت هذا الصّنف، وقمنا بتعيين قيم المُتغيّرات التي تُعتبر مواصفات كلّ سيارة، يُمكننا أخيرا طباعة مواصفات كل سيارة (كائن) وذلك باستدعاء التابع specs. corolla.specs() focus.specs() civic.specs() وكما تتوقّع، المُخرج سيكون كالتّالي: Toyota Corolla speed is 200Km/h and it's brand is Toyota Ford Focus speed is 220Km/h and it's brand is Ford Honda Civic speed is 210Km/h and it's brand is Honda إرجاع الصنف داخل تابع يُمكنك أن تقوم بكتابة تابع داخل صنف، بحيث يقوم التابع بتنفيذ شيفرة ما ثمّ إعادة الصّنف نفسه (ما نُشير إليه بالكلمة self) وبهذا ستتمكّن من استدعاء التابع أكثر من مرّة في سطر واحد، تأمل ما يلي: class Car: def start(self): print 'Starting engine…' return self ما فعلناه في الشّيفرة أعلاه ليس بالشيء الكثير، أولا نقوم بتعريف الصّنف ثمّ بعد ذلك نُعرّف التّابع start المسؤول عن تشغيل السّيارة، وبعدها نطبع جملة تُفيد بأنّ مُحرّك السّيارة قيد التّشغيل، بعد تنفيذ الشيفرة سيُرجع التّابع الصّنف نفسه. ما سيُمكّننا من استدعاء التّابع أكثر من مرّة في سطر واحد كما يلي: corolla = Car() corolla.start().start().start() المُخرج سيكون كالتّالي: Starting engine… Starting engine… Starting engine… كما تُلاحظ لقد نُفّذت الشّيفرة الموجودة داخل التّابع start ثلاث مرّات. قد تتساءل عن أهميّة الأمر، وأتّفق معك في أنّ الأمر يُمكن أن لا يكون مُفيدا. لكنّه يكون مُفيدا إذا أردت أن تقوم بتنفيذ تابعين بشكل تسلسلي، لنقل بأنّ لدينا تابعا آخر باسم move ولنفرض بأنّه سيكون مسؤولا عن تحريك السّيارة، سيكون من الأفضل لو استطعنا أن نستدعي تابع الحركة مُباشرة بعد استدعاء تابع التّشغيل. بحيث يبدو الاستعمال كالتّالي: corolla.start().move() يُمكننا أن نقوم بالأمر ببساطة، وفي الحقيقة لن نحتاج إلا لإضافة التابع move لما كتبناه سابقا. class Car: def start(self): print 'Starting engine…' return self def move(self): print 'The car is moving…' بهذه الطّريقة سنتمكن من تنفيذ الشيفرة دون مشاكل: Car().start().move() المُخرج: Starting engine… Starting engine… The car is moving… لكن لاحظ هذه المرّة بأنّنا لم نُرجع كلمة self في التّابع move ما يعني بأنّنا لن نتمكن من القيام باستدعاء التابع أكثر من مرّة: Car().start().move().move() إذا حاولت أن تُنفّذ الأمر السّابق ستحصل على خطأ كالتّالي: AttributeError: 'NoneType' object has no attribute 'move' وهذا راجع لكوننا لم نُرجع الصنف في آخر التّابع move. الوراثة مفهوم الوراثة في البرمجة كائنيّة التوجه لا يختلف كثيرا عن مفهوم الوراثة في العالم الواقعي، الصّنف الابن يرث جميع المتغيرات والتوابع من الصّنف الأب، يُمكن التّفكير في الأمر على أنّه نسخ. أي أنّ الصّنف الابن مُجرّد نُسخة طبق الأصل من الصّنف الأصلي، الفرق هنا هو أنّك تستطيع أن تُضيف المزيد من الخصائص والتوابع للصّنف الابن. وطريقة إنشاء صنف يرث من صنف آخر هي بوضع الصّنف الأب بين قوسين عند إنشاء الصّنف الابن. انظُر المثال التّالي: class Parent: a = 1 b = 2 class Child(Parent): c = 3 في المثال أعلاه، قُمنا بإنشاء صنف باسم Parent مع مُتغيّرين a و b، وبعدها أنشأنا صنفا Child الذي يرث خصائص الصّنف السابق، وبالتّالي فسنتمكّن من الوصول إلى مُتغيّرات الصّنف Parent من خلال الصّنف Child، ما يعني بأنّ الشيفرة التّالية ستعمل دون مشاكل: child = Child() print child.a print child.b print child.c المُخرج: 1 2 3 كما تُلاحظ فإنّنا قد استطعنا الوصول إلى المتغيّرين a و b رغم أنّهما لم يُعرّفا مُباشرة داخل الصّنف Child. يُمكن أن نجعل صنفا يرث توابع من صنف آخر بنفس الطّريقة: class Person: name = 'Person' def say_hello(self): name = self.name print 'Hello my name is {}'.format(name) class Abdelhadi(Person): name = 'Abdelhadi' استدعاء التّابع say_hello من كائن من الصّنف Abdelhadi. me = Abdelhadi() me.say_hello() المُخرج: Hello my name is Abdelhadi في الشيفرة أعلاه، قُمنا أولا بتعريف صنف "شخص" باسم Person ثمّ عرّفنا المُتغيّر name داخل الصّنف، وأعطيناه قيمة افتراضيّة، بعدها عرّفنا التّابع say_hello ليطبع جُملة التّرحيب. متغيرات الصنف المبنية مسبقا توجد مُتغيّرات مبنية مُسبقا في لغة بايثون، وتُسمى هذه المُتغيّرات بمُتغيّرات الصّنف، وما نعنيه بأنّها مبنية مُسبقا هو أنّك لا تحتاج إلى تعريفها، وتمتاز هذه المُتغيّرات بأنّها مُحاطة بتسطيرين سُفليّين Underscores وإليك بعضا من هذه المُتغيّرات: __doc__ سلسلة توثيق الصنف (Documentation string) وهو ما يُكتب مُباشرة بعد تعريف الصنف، ويحتوي في الغالب معلومات حول وظيفة الصّنف. القيمة الافتراضية لهذا المتغير هي None ألق نظرة على الصّنف التالي: class Person: ''' This class does nothing ''' pass لاحظ بأنّ سلسلة التوثيق مُحاطة بثلاث علامات تنصيص إذا أردنا الوصول إلى هذا التوثيق فكلّ ما علينا فعله هو استخدام الصّفة __doc__: print Person.__doc__ المُخرج: This class does nothing __module__ الوحدة التي عُرّفَ فيها الصّنف، وقيمتها الافتراضية هي'__main__'. print Person.__module__ المخرج: __main__ __dict__ هذا المُتغيّر عبارة عن قاموس يحمل ما سبق كقيم بدئية، كما يحمل أسماء المتغيّرات التي تُنشؤها أنت وقيمها وكذلك أسماء التوابع، لاحظ المثال التالي (نفّذه مُباشرة على مُفسّر بايثون لتُلاحظ النّتيجة): >>> class Math: ... x = 1 ... y = 2 ... def x_plus_y(self): ... return self.x + self.y ... >>> Math.__dict__ {'y': 2, 'x': 1, '__module__': '__main__', '__doc__': None, 'x_plus_y': <function x_plus_y at 0x7f186e76a6e0>} كما تُلاحظ مُخرجات القاموس تحتوي على كل من قيم المُتغيرات الخاصّة بالصّنف سواء التي عرّفناها أو المبنية مُسبقا، لاحظ كذلك العنصر الأخير من القاموس: 'x_plus_y': <function x_plus_y at 0x7f186e76a6e0>} هذا العنصر يحمل مفتاحا باسم التابع x_plus_y الذي عرّفناه، وقيمة هذه المفتاح تُشير إلى أنّه دالة. __name__ اسم الصّنف، يُمكنك تطبيقه على أي صنف للحصول على اسمه، انظر المثال التالي: >>> a = Math >>> a.__name__ 'Math' كما تُلاحظ فقد حصلنا على اسم الصنف Math. الوصول إلى الصنف الخاص بكائن ما المُتغيّرات التي ذكرناها سابقا خاصّة بالصّنف فقط ولا يُمكن الوصول إليها من كائن من هذا الصّنف. تأمّل الصّنف التالي: class Person: name = 'Abdelhadi' لننشئ الآن كائنا من هذا الصّنف: me = Person() يُمكننا الآن الوصول إلى قيمة المُتغيّر name كالتالي: >>> me.name 'Abdelhadi' لكننّا لن نستطيع الوصول إلى مُتغيّرات الصّنف من الكائن، بل من الصّنف فقط: person_class = Person person_class.__name__ # الشيفرة صحيحة person_object = Person() person_object.__name__ # الشيفرة خاطئة لأنّنا نحاول الوصول إلى مُتغيّر غير مُعرّف داخل الكائن ستحصل على خطأ من نوع AttributeError عند تنفيذ السّطر الأخير: AttributeError: Person instance has no attribute '__name__' الحل الأمثل هو أن نصل إلى الصّنف انطلاقا من الكائن، وبعدها سنتمكّن من الوصول إلى مُتغيّرات/صفات الصّنف دون مشاكل، وطريقة الوصول إلى صنف كائن هي بإلحاقه بكلمة __class__، انظر المثال التّالي (سنعتمد على الصنف الذي أنشأناه أعلاه): person_object = Person() person_object_class = person_object.__class__ # إسناد صنف الكائن لمتغيّر print person_object_class.__name__ # اسم الصّنف print person_object_class.__dict__ # قاموس يحتوي على بيانات الصّنف بدأنا بإنشاء كائن person_object ثمّ استخدام جُملة __class__ للوصول إلى الصّنف. المُخرج: Person {'__module__': '__main__', 'name': 'Abdelhadi', '__doc__': None} يُمكنك كذلك القيام بالأمر داخل تابع في الصّنف: class Person: def say_hello(self): print 'Hi I am a {}'.format(self.__class__.__name__) person_obj = Person() person_obj.say_hello() لاحظ استخدام self.__class__.__name__ للوصول إلى اسم الصّنف في التّابع say_hello. المُخرج: Hi I am a Person التوابع الخاصة يُمكن استخدام بعض التّوابع الخاصّة لتنفيذ شيفرة عند القيام بإجراء معيّن، وإليك قائمة بأهم هذه التّوابع (لاحظ بأنّها مُحاطة بتسطيرين سُفليّين Underscores). التابع init: تُنفَّذُ الشيفرة التي بداخله عند إنشاء كائن من الصّنف، ويُسمّى أيضا بتابع البناء Contractor . التابع del: تُنفّذ شيفرته عند حذف كائن أو عند استدعاء دالة الخروج exit. التابع repr: تُنفّذ الشيفرة عند استدعاء الكائن، ويُستخدم هذا التابع لإرجاع معلومات حول الكائن في الوضع التّفاعلي (من مُفسّر لغة بايثون مُباشرة) مثال: # استدعاء __init__ person_obj = Person() # استدعاء __repr__ person_obj repr(person_obj) # حذف الكائن واستدعاء __del__ del(person_obj) التابع init هذا مثال بسيط على إنشاء واستدعاء هذا التّابع: class Person: def __init__(self): print 'A new object was created' me = Person() مُخرج البرنامج سيكون جُملة تُفيد بأنّ كائنا قد أنشئ، لاحظ أنّ الجملة A new object was created قد طُبعت رغم عدم استدعاء التّابع init بشكل صريح، ويُمكن الحصول على نفس المُخرج باستدعاء التّابع كالتّالي: me.__init__() حسنا تعلّمنا الآن بأنّنا نستطيع تنفيذ الشيفرة الموجودة بداخل التّابع __init__ بمُجرّد إنشاء كائن من الصّنف، ولكن ماذا عن المُعاملات؟ يُمكن تمرير المُعاملات للتّابع __init__ عند إنشاء صنف كالتّالي: me = Person('Abdelhadi') وفي التّابع سيكون الأمر كالتّالي: def __init__(self, name): name = self.name print 'Hello My name is {}'.format(name) الشيفرة الكاملة: class Person: def __init__(self, name): self.name = name print 'Hello My name is {}'.format(name) me = Person('Abdelhadi') المُخرج: Hello My name is Abdelhadi التابع repr يُستعمل هذا التّابع لإرجاع معلومات قابلة للطّباعة، ويُستخدم كثيرا في الوضع التّفاعلي (مُفسّر بايثون)، ويجب أن تكون القيمة المُرجعَةُ عبارة عن سلسلة نصيّة. وإليك مثالا لكيفيّة استخدامه: class Math: x = 1 y = 2 def __repr__(self): x = self.x y = self.y return 'x: {}, y: {}'.format(x, y) x_y = Math() print x_y المُخرج: x: 1, y: 2 يُمكننا الاستفادة من هذا التّابع للوصول إلى اسم الصنف عند الوراثة: # تعريف الصّنف الرّئيسي class Person: def __repr__(self): return 'Hi I am a {}'.format(self.__class__.__name__) # الوراثة class Writer(Person): pass class Student(Person): pass # إنشاء الكائنات omar = Student() abdelhadi = Writer() # طباعة قيّم التّابع repr print omar print abdelhadi المُخرج: Hi I am a Student Hi I am a Writer لاحظ بأنّ الصّنفين Writer و Student لا يحتويان على أية شيفرة، ومع ذلك فقد نُفّذ التّابع repr واستجاب بطريقة مُختلفة مع كلّ كائن. التابع del تُنَفّذُ الشّيفرة الموجودة بداخل هذا التّابع عند حذف كائن باستعمال الدّالة del وهي دالة تُستعمل لإنجاز ما يُسمى بعمليّة جمع القُمامة Garbage Collecting والهدف الرئيسي من هذه العمليّة هو تحرير الذاكرة، ولحذف كائن يكفي أن تقوم باستدعاء الدّالة del مع تمرير الكائن كمُعامل: class Math: x = 1 y = 2 numbers = Math() numbers.x # ==> 1 numbers.y # ==> 2 # حذف الكائن del(numbers) # خطأ numbers.x # ==> NameError: name 'numbers' is not defined بعد أن حذفنا الكائن numbers لم يعد بإمكاننا الوصول إلى قيمة المُتغيّر x، وأسفر الأمر عن خطأ من نوع NameError. يُستدعى التابع del مُباشرة بعد حذف الكائن، ما يعني بأنّنا نستطيع أن نطبع جملة تُفيد بأنّ الكائن قد حُذف: class Math: x = 1 y = 2 def __del__(self): print 'Object deleted!' numbers = Math() numbers.x # ==> 1 del(numbers) المُخرج: Object deleted! تُنفَّذ شيفرة __del__ كذلك عند استدعاء الدالة exit للخروج من البرنامج: class Math: x = 1 y = 2 def exit_program(self): exit() def __del__(self): print 'Object deleted!' numbers = Math() numbers.exit_program() مُخرَج البرنامج أعلاه سيكون نفس الشيء رغم عدم حذف الكائن. والسبب راجع لاستخدام الدّالة exit داخل التّابع exit_program. تمارين تمرين 1 عد إلى الدّروس السّابقة وحاول تحويل برنامج تسجيل الدّخول إلى برنامج سهل القراءة والاستخدام باستعمال البرمجة كائنية التوجه، فمثلا يُمكن أن يستعمل من قبل مُبرمجين آخرين بالطّريقة التّالية: user = User() user.signup('username', 'password', 'password_confirmation') user.login('username', 'password') user.say_hello() user.logout() تمرين 2 أنشئ برنامجا يطبع جميع التوابع والمُتغيّرات الموجودة داخل الصّنف الذي أنشأته كحل للتّمرين الأول.
  12. النّصيحة الوحيدة التي سأقدّمها لك هي عدم الاعتماد على الشّعر وحده، إذ تستطيع التواصل مع أشخاص من أنحاء العالم، والوسائل التّعليميّة مُتاحة مجانا (كهذا الموقع مثلا). يُمكنك قراءة المقالات المُتواجدة في هذا الموقع، وقراءة الأجوبة المطروحة في هذا القسم حتى تبني فهما أساسيّا لمبدأ الكتابة على الأنترنت، وبالتّالي ستتمكّن من تقديم خدمات كتابة المقالات في شتى المجالات. تحديد المجال الأقرب إليك سيكون بعد بضعة أسابيع من القراءة والكتابة، وبعد ذلك ستتمكّنُ من تقديم عروض العمل على المشاريع المطروحة على موقع مُستقل، ويُمكنك تقديم خدمات كتابة المقالات على خمسات. بما أنّ لك خبرة في اللغة العربيّة فلن تُواجه مشاكل المبتدئين الذين تكون جملهم ركيكة خاليّة من المعنى.
  13. @samer_jabal لغة XML تُستعمل في برمجة تطبيقات الأندرويد لعرض عناصر الواجهة الرّسوميّة (الأزرار، حقول النّص، خانات الإختيار...) جنبا إلى جنبا مع لغة جافا تماما مثل HTML مع Django أو Flask، لكنّني لا أعرف إن كان الأمر مُمكنا مع لغة بايثون، ما أعرفُه هو أنّك تستطيع تطوير تطبيقات للأندرويد بإطار العمل Kivy أو باستخدام API بحيث تُطور التّطبيق بأحد أطر العمل الخاصّة بالويب، وبعدها تستعمل Java كطريقة لإرسال طلبات واستقبال الأجوبة (يعني تقريبا تحويل تطبيق ويب مُطوّر بلغة Python إلى تطبيق أندرويد باستخدام عارض صفحات الويب فقط).
  14. تعرّفنا فيما سبق من دروس هذه السلسلة على أساسيات لغة بايثون، من مُتغيّرات وحلقات تكرار إلى الدوال، وقد حان الوقتُ للدخول إلى أساسيات البرمجة كائنية التوجّه Object Oriented Programming وهي ببساطة طريقة أخرى للبرمجة بحيث تكون أجزاء الشّيفرة مجموعة داخل دوال تُسمّى التوابع methods والدوال تكون داخل صنف معيّن Class. عند إنشاء كائن object من هذا الصنف فإنّنا نستطيع أن نُنفّذ عليه مُختلف العمليات الموجودة داخل التوابع والتي بدورها توجد داخل الصنف. هناك تسميّات عربية أخرى لهذا النّوع من البرمجة، لذا لا تقلق إذا صادَفتَ أحد هذه التّسميات في أماكن أخرى، فكلّها تُشير إلى نفس المعنى: برمجة غرضيّة التوجه برمجة شيئية المنحى برمجة كائنيّة المنحى بنية الصنف Class الصنف ببساطة يحتوي على أجزاء مُختلفة من الشيفرة تماما مثل الدالة، الفرق هنا هو أنّ الصنف يحتوي على دوال كذلك، وهذه الدوال تُسمى التّوابع، ويحتوي كذلك على مُتغيّرات وتنقسم هذه الأخيرة إلى نوعين، مُتغيّر الصنف، والمُتغيّر العادي، الفرق بينهما هو أنّك تستطيع الوصول إلى مُتغيّر الصنف في أي مكان داخل الصنف (سواء داخل التوابع أو خارجها). إنشاء صنف لإنشاء صنف في لغة بايثون كلّ ما عليك فعله هو كتابة كلمة class وبعدها اسم الصنف ثمّ إلحاق نقطتين، بعدها اكتب الشيفرة بإزاحة أربع مسافات: >>> class My_class: ... pass أنشأنا أعلاه صنفًا بسيطا باسم My_class وهو لا يفعل أي شيء يُذكر (كلمة pass تُخبر بايثون بالمرور دون تنفيذ أي شيء). إذا كتبت اسم الصنف على مفسّر بايثون فستجد مُخرجا كالتّالي: >>> My_class <class __main__.My_class at 0x7fd0efc41460> لاحظ بأنّ الكلمة الأولى من المُخرج هي class أي أنّنا أصبحنا نمتلك صنفًا جديدًا، ما يتبع at هو المكان في الذاكرة الذي وُضع فيه الصنف ويتغيّر بين الحين والآخر. إنشاء كائن من صنف بعد أن أنشأنا الصنف سنتمكّن الآن من إنشاء كائن من هذا الصنف، والكائن مُجرّد اسم تماما كالمُتغيّر: my_object = My_class() الآن الكائن my_object هو من صنف My_class. تعريف المتغيرات داخل صنف يُمكننا أن نُعرّف مُتغيّرات في الصنف تماما كما نُعرّف المُتغيّرات بشكل عادي. class My_class: my_variable = 'This is my variable' للوصول إلى المُتغيّر ننشئ أولا كائنا من الصنف وبعدها نكتب اسم الكائن ثمّ نقطة ثمّ اسم المُتغيّر: my_object = My_class() print my_object.my_variable المُخرج: This is my variable يُمكن كذلك الحصول على النّتيجة ذاتها في سطر واحد: print My_class().my_variable إنشاء التوابع التوابع هي دوال خاصّة بالصنف، ويُمكننا إنشاء التابع بنفس الطّريقة التي نُنشئ بها الدالة، الإختلاف هنا هو أنّ جميع التوابع يجب أن تُعرّف مع مُعامل باسم self وذلك للإشارة إلى أنّ الدالة/التابع تابع للصنف، لننشئ تابعا داخل صنف الآن. class My_class: my_variable = 'This is my variable' def my_method(self): print 'This is my method' الآن إذا أنشأنا كائنا فإنّنا سنتمكّن من الوصول إلى التابع، وتذكّر بأنّ التابع تلحقه الأقواس: my_object = My_class() my_object.my_method() المُخرج: This is my method يُمكن كذلك الحصول على النّتيجة ذاتها في سطر واحد: My_class().my_method() كما تُلاحظ فقد نُفّذت الشيفرة الموجودة داخل التّابع my_method ويُمكننا كذلك أن نجعل التّابع يقبل المُعاملات، لكن تذكّر الحفاظ على الكلمة self كمُتغيّر أول. class My_class: my_variable = 'This is my variable' def my_method(self, my_parameter): print 'This is my method ; {} is my parameter'.format(my_parameter) يُمكنك استدعاء التّابع كالتّالي: my_object = My_class() my_object.my_method('Parameter1') my_object.my_method('Parameter2') المُخرج: This is my method ; Parameter1 is my parameter This is my method ; Parameter2 is my parameter في البرنامج السّابق، أنشأنا أولا صنفًا باسم My_class وقُمنا بتعريف مُتغيّر، ثمّ بتعريف تابع باسم my_method يقبل مُعاملين self و my_parameter، بالنّسبة لاستدعاء التّابع، فنحتاج فقط إلى تمرير المُعاملات الموجودة بعد المُعامل selfولا نحتاج إلى تعيين قيمة لهذا المُعامل. مُلاحظة: يُمكنك إعادة تسميّة المُعامل الأول كما تشاء، أي أنّ البرنامج التّالي سيعمل دون مشاكل. class My_class: def my_method(this, my_parameter): print '{} is my parameter'.format(my_parameter) ولكن رغم ذلك فالمُتعارف عليه بين مُبرمجي لغة بايثون هو استعمال self، وفي كثير من اللغات الأخرى تُستعمل this عوضا عن self، أما في برامِجك فمن المُفضّل الإبقاء على هذه التّسميّة المُتعارف عنها، وذلك لتكون شيفراته سهلة القراءة. الوصول إلى متغيرات الصنف داخل التوابع تأمّل الصنف التّالي: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) البرنامج أعلاه بسيط جدا، أولا نعرّف صنف باسم Person وبعدها نقوم بتعيين قيمتين للمُتغيّرين name و lastname، وبعدها عرّفنا تابعا باسم say_hello يطبع جملة Hello, My name is Abdelhadi. كلّ شيء جيد، لكن ماذا لو أردنا أن نصل إلى المُتغيّرات الأخرى الموجودة خارج التّابع، فلا يُمكننا مثلا أن نقوم بالأمر كالتّالي: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print lastname print job ستحصل على الخطأ التّالي: global name 'lastname' is not defined لتفادي هذا الخطأ سنستعمل كلمة self قبل المُتغيّر. class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print 'My Last name is {} '.format(self.lastname) print 'I am a {}'.format(self.job) استدعاء التّابع: me = Person() me.say_hello() المُخرج: Hello, My name is Abdelhadi My Last name is Dyouri I am a Writer, Developer لاحظ بأنّنا قُمنا بالوصول إلى مُتغيّر lastname عن طريق استدعائه بـ self.lastname وكذا الحال مع المُتغيّر job، وهذه الطّريقة مُشابهة لاستخدام كلمة global الفرق هنا أنّ هذه الأخيرة تُمكن من الوصول إلى المُتغيّر في كامل البرنامج، أمّا كلمة self فتُشير إلى المُتغيّر المُعرّف في الصنف الحاليّة فقط. لتفهم أكثر كيفيّة عمل الكلمة self فقط تخيّل بأنّها تحمل نفس اسم الصنف، مثلا: class Person: lastname = 'Dyouri' job = 'Writer, Developer' def say_hello(self): name = 'Abdelhadi' print 'Hello, My name is {}'.format(name) print 'My Last name is {} '.format(Abd.lastname) print 'I am a {}'.format(Abd.job) لاحظ بأنّنا غيّرنا كلمة self إلى اسم الصنف واستمرّ عمل البرنامج دون مشاكل. وبنفس الطّريقة يُمكنك أن تستدعي تابعا داخل تابع آخر في نفس الصنف: class Person: def say_name(self): print 'Abdelhadi' def say_hello(self): print 'Hello My name is:' self.say_name() المُخرج: Hello My name is: Abdelhadi ما حدث هو أنّ التّابع say_hello قام بطباعة جملة :Hello My name is ثمّ قام باستدعاء التّابع say_name الذي قام بدوره بطباعة الاسم Abdelhadi. لماذا تستعمل البرمجة الكائنية، ومتى يجب علي استخدامها قد تُلاحظ بأنّ ما يُمكنك فعله بالبرمجة الكائنيّة يُمكن القيام به بالدوال والمُتغيّرات فقط. هذا صحيح، وهو أمر واضح، البرمجة الكائنيّة تُستعمل أساسا إذا كان البرنامج الذي تبنيه مُعقّدا مع العديد من الوظائف المُتعلّقة ببعضها (كمكتبة برمجيّة)، مثلا لنقل بأنّك تُطوّر برنامجا مسؤولا عن جلب بيانات من موقع مُعيّن، وبعدها التّعديل عليها، ثمّ إخراج مستند PDF يحتوي على هذه البيانات بشكل يُسهّل قراءتها، هنا ستحتاج إلى صنف لجلب البيانات والتّعديل عليها، وصنف أخرى لتحويل البيانات إلى نصّ مقروء واضح ثمّ إلى ملفّ PDF. إذا نشرت برنامجك مع صديقك وأراد أن يعمل على الجزء الثاني لإضافة وظيفة تُمكن المُستخدم من طباعة المُستند فلا يُعقل أن يضطر للمرور على كل ما يتعلّق بجلب البيانات فقط لأنّه يريد أن يضيف خاصيّة لا علاقة لها بجلب البيانات. استعمال البرمجة الكائنيّة في هذا المشروع سيسمح لك بالتّركيز على الجزء الأول، وسيسمح لصديقك بالتّركيز على تطوير الجزء الثاني. خلاصة الأمر هي أنّك لست مضطرا لاستعمال البرمجة الكائنيّة إلا إذا كان برنامجك طويلا يحتوي على وظائف تتعلّق ببعضها البعض (وظائف من نفس الصنف)، ونسبة استخدام الآخرين لشيفرتك عالية. تمارين التمرين 1 أنشئ صنفًا باسمك، وقم بتعريف مُتغيّرين lastname (الاسم العائلي) و age (العمر)، ثم أنشئ كائنا باسم me (أنا) وقم بطباعة اسمك العائلي وعمرك. التمرين 2 أنشئ صنفًا باسم Car (سيارة) وقم بتعريف مُتغيّرات لصفات السّيارة، مثلا brand لاسم الشّركة، release_date لتاريخ الإعلان عن السّيارة. التمرين 3 أضف توابع إلى الصنف Car التي أنشأتها في التّمرين الثّاني، يُمكن أن تكون التوابع عبارة عن عمليّات تقوم بها السّيارة مثلا move للحركة، stop للتوقّف، slow_down لتخفيض السّرعة، وقم بطباعة جمل تفيد بأنّ العمليّة قد نجحت. المفروض أن يتمكّن الآخرون من إنشاء كائنات خاصّة بهم بحيث تُستخدم بهذه الطّريقة: bmw = Car() bmw.move() bmw.slow_down() bmw.stop() خاتمة تعرّفنا في هذا الدّرس على بعض من أهم أساسيات البرمجة الكائنيّة التوجه في لغة بايثون وهذا الموضوع أطول من أن أشرحه في درس واحد لذلك سيكون الدّرس التّالي تكملة لما تعلّمناه في هذا الدّرس حول تعلم بايثون وسيغطي مفاهيم أخرى حول البرمجة كائنية التوجّه.
  15. تعلمنا إلى الآن مُعظم أساسيات لغة بايثون، تعلمنا كيفية التعامل مع أنواع البيانات المُختلفة، الحصول على مُدخلات من المُستخدم، التعابير الشرطية، حلقات التكرار واستعمال الدوال في برنامجنا لمرونة أكثر. وسنتعلم اليوم كيفيّة التعامل مع الملفات النّصية في لغة بايثون، كيفية الكتابة على ملف، وكيفية قراءة مُحتويات ملف مُعيّن. ما معنى Files I/O؟ ترجمة File هي "ملف" أما I/O فهو اختصار لـكلمتي Input و Output اللتان تعنيان المُدخل والمُخرج على التوالي. تعرفنا إلى الآن على دالتين للقيام بهاتين العمليتين الأولى هي الدالة print للإخراج والدالة raw_input للإدخال. لكنّ هذه الدوال لا تعمل إلا أثناء تشغيل البرنامج، فبعد انتهاء تنفيذه سيعود كل شيء إلى طبيعته وستفقد البيانات التي طبعتها أو التي حصلت عليها من المُستخدم. ماذا لو أردت أن تحتفظ بالبيانات في ملف ما؟ هذا بالضبط ما سنتعلمه اليوم. وإليك مُخطّطا لهذا الدرس: فتح ملف في لغة بايثون. أنماط الوصول Access modes. الكتابة على الملف. قراءة الملف. الدوال المُساعدة عند فتح ملف. إغلاق الملف. الجملة format سأستعمل في هذا الدّرس جملة جديدة لم يسبق لنا أن تحدّثنا عنها في الدروس السابقة وهي جملة format التي تُعتبر بمثابة مُحوّل لأنواع القيم المُختلفة إلى قيمة نصيّة، وتُساعد على دمج أي نوع داخل سلسلة نصّية دون الحاجة إلى تحويله بالدالة str. انظر المثال التالي: >>> '{0}, {1}, {2}'.format('a', 'b', 'c') 'a, b, c' يُمكن تبسيط الشيفرة أعلاه لتكون كالتّالي: >>> '{}, {}, {}'.format('a', 'b', 'c') إليك مثالا لطريقة الاستفادة منها في الواقع: >>> print 'Hello {} : {} : {} : {}'.format('Abdelhadi', 4, 22.4, True) Hello Abdelhadi : 4 : 22.4 : True لاحظ أنّ المُعاملات المُمرّرة للدالة قد أخذت مكان العلامات {} رغم أنّ كلّ قيمة ذات نوع مُختلف عن الأخرى. الطّريقة أعلاه أفضل بكثير من الطّريقة التّقليدية: >>> print 'Hello', 'Abdelhadi', ':', str(4), ':', str(22.4), ':', str(True) Hello Abdelhadi : 4 : 22.4 : True ويُمكنك أيضا أن تؤدي أغراضا أخرى بهذه الجملة، مثلا يُمكنك أن توزع سلسلة نصيّة على أماكن مُعيّنة في السّلسلة كالتالي: >>> '{0}, {1}, {2}'.format(*'abc') 'a, b, c' تستطيع تغيير ترتيب العناصر ببساطة: >>> '{2}, {1}, {0}'.format(*'abc') 'c, b, a' ويُمكنك أيضا أن تدمج عناصر القواميس في السلاسل النّصيّة بسهولة وذلك بالطّريقة التالية: >>> table = {'Abdelhadi': 1929, 'Ahmed': 1222, 'Omar': 1320} >>> print 'Ahmed: {Ahmed:d}; Abdelhadi: {Abdelhadi:d}; Omar: {Omar:d}'.format(**table) Ahmed: 1222; Abdelhadi: 1929; Omar: 1320 لاحظ النّجمتين ** قبل الاسم table. فتح ملف في لغة بايثون قبل أن نتعامل مع أي ملف يجب علينا أولا أن نفتحه، تماما كما تفتح أي ملف نصي للتعديل عليه بمُحرّر النّصوص. لكننا في هذا الدرس سنستعمل لغة بايثون للقيام بذلك. وتوفر لغة بايثون دالة open لفتح الملفات، والتي يُمكنك أن تستخدمها بالشكل التالي: open('filename.txt', 'Access mode') المعامل الأول المُعامل الأول هو اسم الملف، لاحظ أنّك تستطيع استبدال filename بأي اسم تريده ويُمكنك حتى أن تستبدل الامتداد، فمثلا يُمكنك أن تستعمل امتداد ملفات بايثون py بحيث تُصبح الدالة كالتالي: open('python_file.py', 'Access mode') الملف يجب أن يكون في مُجلّد العمل، إذا كنت تستعمل نظام GNU/Linux أو نظام Mac وفتحت مُفسر بايثون أو نفّذت ملفا بامتداد py فالملف يجب أن يكون في المجلد الذي قمت بالعملية منه (في الغالب يكون مجلّد المنزل home). أما إذا كنت تستخدم مُفسر لغة بايثون على نظام Windows فسيكون الملف داخل مجلّد تنصيب حزمة Python والذي غالبا ما يكون في القرص C ومجلد باسم PythonNN مع استبدال NN برقم الإصدار الخاص بلغة بايثون، فمثلا لو قمت بتنصيب الإصدار 2.7 فسيكون مسار المُجلد التالي: C:\Python27 إذا واجهت مشاكل في إيجاد مجلّد العمل يُمكنك تنفيذ الشيفرة التالية للحصول على مسار المُجلّد: >>> import os >>> os.getcwd() المُخرجات ستكون كالتالي (حسب نظام التّشغيل واسم المُستخدم لديك): '/home/dyouri' يُمكنك حفظ المُخرج في مُتغير إذا أردت ذلك. ولا تقلق إذا لم تفهم الأسطر السابقة، إذ سنتحدّث عنها في درس لاحق. المعامل الثاني بالنّسبة للمعامل الثاني (Access mode ) فهو نمط الوصول الذي ترغب بفتح الملف به وهو إما للكتابة أو للقراءة أو كليهما. انظر الفقرة التالية. أنماط الوصول Access modes عند فتح ملف في لغة بايثون، تكون مطالبا بتوفير معاملين للدالة open المُعامل الأول هو اسم الملف، والمعامل الثاني هو نمط الوصول، أي السبب الذي فتحت الملف من أجله، وهناك العديد من الخيارات لذلك، وإليك أهم الخيارات التي يجب عليك أخذها بعين الاعتبار حسب هدفك من فتح الملف: فتح الملف للقراءة r: هذا هو النمط الافتراضي إذا لم تحدد المُعامل الثاني في دالة open ويُمكنك من قراءة ملف بأحد الدوال المُتاحة لذلك (انظر فصل قراءة الملف). مثال: open('file.txt', 'r') r+: يُفتح الملف في هذا النمط للقراءة والكتابة معا، مع وضع مؤشّر الفأرة في بداية الملف. مثال: open('file.txt', 'r+') فتح الملف للكتابة w: يُفتح الملف فقط للكتابة، إذا كان الملف موجودا فإنّه يكتب عليه (Overwrite) أي أنّك ستفقد البيانات الأصلية. إذا لم يكن الملف موجودا فإنّ الدالة تنشئ ملفا جديدا للكتابة. مثال: open('file.txt', 'w') w+: فتح الملف لكل من الكتابة والقراءة، إذا كان الملف موجودا فستتم الكتابة عليه (Overwrite). إذا لم يكن الملف موجودا فإنّ الدالة تنشئ ملفا جديدا للكتابة والقراءة. مثال: open('file.txt', 'w+') a: إذا كان الملف موجودا أصلا، فسيُفتح للإلحاق Appending، أي أنّ مُكونات الملف تبقى كما هي، إذا أضفت أي نصّ فسيُضاف في آخر الملف. إذا لم يكن الملف موجودا فسيتم إنشاء ملف جديد. مثال: open('file.txt', 'a') a+: إذا كان الملف موجودا فإنّه يُفتح لكل من الإلحاق والقراءة، إذا لم يكن موجودا فسيتم إنشاء ملف جديد. مثال: open('file.txt', 'a+') الكتابة على الملف بعد فتح الملف سواء أكان موجودا من قبل أو أنّك أنشأته بالدالة نفسها، ستستطيع الكتابة على الملف وإضافة نصوصك الخاصّة، وذلك بالدالة write مع مُعامل عبارة عن سلسلة نصية، انظر المثال التالي: >>> file = open('file.txt', 'w') # فتح الملف وإسناده إلى مُتغير >>> file.write('Hello World!') # الكتابة بالملف >>> file.close() # إغلاق الملف ملاحظة: إذا كنت تستعمل المُفسّر، فلحفظ التعديلات عليك الخروج من المفسّر أو إغلاق الملف والذي يُمكنك القيام به بالدالة close، مع ملاحظة أنّك لن تتمكن من تعديل الملف بعد إغلاقه، أي أنّك ستحتاج إلى فتحه من جديد بالدالة open. لن تُلاحظ أي مُخرجات عند تنفيذك لأي أمر أعلاه، ولكن رغم ذلك فإنّك إذا فتحت الملف file.txt بأي مُحرّر للنّصوص (Notepad مثلا)، فإنّك ستُلاحظ الجملة !Hello World في بداية الملف. لاحظ بأنّنا فتحنا الملف بنمط الكتابة فقط (w)، إذا فتحت الملف بنفس النّمط مُجدّدا فإنّ التغييرات التي ستكتبها ستُغطّي محتويات الملف. أي أنّك إذا نفّذت الأوامر التاليّة فإنّ مُحتويات الملف ستُصبح الجملة !Hello Python عوضا عن جملة !Hello World # -*- coding: utf-8 -*- >>> file = open('file.txt', 'w') # فتح الملف وإسناده إلى مُتغير >>> file.write('Hello Python!') # الكتابة بالملف >>> file.close() # إغلاق الملف يُمكنك إضافة أكثر من مُدخل في كلّ مرّة، المهم أن تكتب على الملف قبل إغلاقه: >>> file = open('file.txt', 'w') >>> file.write('Abdelhadi Dyouri!') >>> file.write('Hsoub Academy!') >>> file.close() بعد تنفيذ الشيفرة أعلاه، ستكون مُحتويات الملف كالتالي: Abdelhadi Dyouri!Hsoub Academy! لاحظ بأنّ لغة بايثون لا يعود إلى سطر جديد في كل مرة، ولكي تحلّ هذه المسألة فيُمكنك ببساطة إضافة الرّمز n\ بين كل سطر، انظر المثال التّالي: >>> file = open('file.txt', 'w') >>> file.write('Abdelhadi Dyouri!\nHsoub Academy!') >>> file.close() مُحتويات الملفّ بعد تقسيم الأسطر بالرّمز n\ هي كالتالي: Abdelhadi Dyouri! Hsoub Academy! لاحظ بأنّ الشيفرة أعلاه لها نفس تأثير ما يلي: >>> file = open('file.txt', 'w') >>> file.write('Abdelhadi Dyouri!\n') >>> file.write('Hsoub Academy!') >>> file.close() كلّ ما تعلمناه إلى الآن هو كيفية إعادة كتابة الملف، ولكن ماذا لو أردت أن تُبقي على محتويات الملف وترغب بإضافة نصوص له؟ يُمكنك ذلك عن طريق تغيير نمط الوصول إلى نمط الإلحاق عوضا عن نمط الكتابة، لنضف المزيد من النّصوص إلى الملف السابق، إلى الآن مُحتوياته هي كالتالي: Abdelhadi Dyouri! Hsoub Academy! لنضف أقسام الأكاديمية إلى آخر الملف، انظر المثال التالي: >>> file = open('file.txt', 'a') >>> file.write('\nFreelance\nEntrepreneurship\nDesign\nApps\nCertificates\nDevops\nMarketing') >>> file.close() لاحظ تغيير المُعامل الثاني في دالة open من w إلى a. بعد تنفيذ الشيفرة أعلاه ستكون مُحتويات الملفّ كالتالي: Abdelhadi Dyouri! Hsoub Academy! Freelance Entrepreneurship Design Apps Certificates Devops Marketing لنُطبّق ما تعلمناه في الدّروس السابقة ولنضع لكلّ قسم عنوان Url الخاص به: افتح ملفا باسم links.py وضع فيه ما يلي: links = [] list = '\nFreelance\nEntrepreneurship\nDesign\nApps\nCertificates\nDevops\nMarketing' list = list.lower().split('\n') for item in list: list = item.replace(item , 'https://academy.hsoub.com/{}'.format(item)) links.append(list) file = open('file.txt', 'w') file.write('Academy: {}'.format(links[0])) file.write('\nFreelance: {}'.format(links[1])) file.write('\nEntrepreneurship: {}'.format(links[2])) file.write('\nDesign: {}'.format(links[3])) file.write('\nApps: {}'.format(links[4])) file.write('\nCertificates: {}'.format(links[5])) file.write('\nDevops: {}'.format(links[6])) file.write('\nMarketing: {}'.format(links[7])) file.close() أولا عرّفنا قائمة روابط فارغة باسم links بعدها أسندنا الأقسام إلى مُتغيّر باسم list ثمّ حولنا الأحرف إلى أحرف صغيرة و قسّمنا السّلسلة النّصية، بعدها استعملنا الحلقة for للدوران على عناصر القائمة، وفي كلّ مرّة نستبدل قيمة القسم برابطه ونُلحق العنصر النّاتج إلى القائمة links وبعدها فتحنا الملف ووضعنا لكل قسم رابطه الخاص وأخيرا أغلقنا الملف. بعد تنفيذ الشيفرة أعلاه ستكون مُحتويات الملف file.txt كالتالي: Academy: https://academy.hsoub.com/ Freelance: https://academy.hsoub.com/freelance Entrepreneurship: https://academy.hsoub.com/entrepreneurship Design: https://academy.hsoub.com/design Apps: https://academy.hsoub.com/apps Certificates: https://academy.hsoub.com/certificates Devops: https://academy.hsoub.com/devops Marketing: https://academy.hsoub.com/marketing يُمكنك أن تجعل البرنامج أكثر ذكاء وذلك باستبدال قائمة الأقسام بمُدخل من المُستخدم وتُضيف رابط القسم إلى الملف، على سبيل المثال سيعمل البرنامج كالتالي: Enter the name of the category: مثلا لنُدخل programming للحصول على رابط قسم البرمجة، يجب أن تكون المُخرجات شيئا كالتالي: Enter the name of the category: programming Ok! Added https://academy.hsoub.com/programming to file.txt هل رأيت حلاوة البرمجة؟ تذكّر بأنّ خيالك هو حدود ما تستطيع القيام به. قراءة الملف الدالة read بعد الكتابة على الملف سيتوجّب علينا قراءته، كما الكتابة تُوفّر لنا لغة بايثون دالة خاصة بالقراءة وهي الدالة read التي يُمكننا أن نستعملها كالتالي: file = open('file.txt', 'r') file.read() لاحظ أنّنا غيّرنا المُعامل الثاني من w إلى r وذلك لأنّنا نرغب بفتح الملفّ للقراءة. بعد تنفيذ الشيفرة أعلاه ستكون المُخرجات كالتالي: 'Academy: https://academy.hsoub.com/\nFreelance: https://academy.hsoub.com/freelance\nEntrepreneurship: https://academy.hsoub.com/entrepreneurship\nDesign: https://academy.hsoub.com/design\nApps: https://academy.hsoub.com/apps\nCertificates: https://academy.hsoub.com/certificates\nDevops: https://academy.hsoub.com/devops\nMarketing: https://academy.hsoub.com/marketing\n' إذا عاودت تنفيذ الدالة read من جديد فإنّ المُخرج سيكون سلسلة نصيّة فارغة'' وهذا يدلّ على أنّنا وصلنا إلى نهاية الملفّ، ولكي تعود إلى بداية الملف من جديد فعليك فتحه مُجدّدا بالدالة open. كما تُلاحظ فإنّ المُخرجات لا تبدو جميلة أبدا، يُمكنك أن تقرأ أسطر الملف باستخدام جملة for عوضا عن الدالة read وستكون المُخرجات نظيفة هذه المرّة. file = open('file.txt', 'r') for line in file: print line ستكون المُخرجات كالتالي: Academy: https://academy.hsoub.com/ Freelance: https://academy.hsoub.com/freelance Entrepreneurship: https://academy.hsoub.com/entrepreneurship Design: https://academy.hsoub.com/design Apps: https://academy.hsoub.com/apps Certificates: https://academy.hsoub.com/certificates Devops: https://academy.hsoub.com/devops Marketing: https://academy.hsoub.com/marketing يُمكنك أيضا تحديد كم من بايت Byte ترغب بقراءته من بداية الملف. فمثلا إذا كنت ترغب بقراءة أول 8 بايت من مُحتويات الملف، فيُمكنك تمرير العدد 8 إلى الدالة read كمُعامل: >>> file = open('file.txt', 'r') >>> file.read(8) 'Academy:' >>> file.close() المُخرجات ستكون كالتالي (أول 8 أحرف من الملف): 'Academy:' لاحظ بأنّ البايت الواحد لا يعني دائما الحرف الواحد إذ أنّ هناك استثناءات عديدة، فمثلا في اللغة العربية الحرف الواحد يشغل حوالي 2 بايت أما الأحرف الصينية فتشغل حوالي 3 بايت. في الأحرف الإنجليزية عادة ما يُساوي الحرف الواحد 1 بايت فقط. الدالة readline يُمكنك أن تقرأ سطرا واحدا من الملف في كلّ مرّة، وذلك بالاعتماد على جُملة readline عوضا عن جملة read. >>> file = open('file.txt', 'r') >>> file.readline() 'Academy: https://academy.hsoub.com/\n' >>> file.readline() 'Freelance: https://academy.hsoub.com/freelance\n' >>> file.readline() 'Entrepreneurship: https://academy.hsoub.com/entrepreneurship\n' لاحظ بأنّ المُخرج يكون مُختلفا في كلّ مرّة تُستَدعَى فيها الدالة readline إذ أنّ المُخرج يكون سطرا واحدا في كل مرة، وفي المرّة التالية تكون النّتيجة السّطر التالي إلى نهاية الملفّ. في النّهاية سيكون المخرج سلسلة نصيّة فارغة: >>> file.readline() 'Marketing: https://academy.hsoub.com/marketing\n' >>> file.readline() '' إذا أردت العودة إلى بداية الملفّ من جديد فيُمكنك ذلك إما بإغلاق الملف وفتحه من جديد أو باستعمال الدالةّ seek مع تمرير عدد البايتات الذي ترغب بتخطّيه، (أي صفرا للعودة إلى بداية الملف). >>> file.readline() '' >>> file.seek(0) >>> file.readline() 'Academy: https://academy.hsoub.com/\n' يُمكن مثلا العودة إلى أول 8 بايت من الملف بالطّريقة التالية: >>> file.seek(8) >>> file.readline() ' https://academy.hsoub.com/\n' يُمكنك أن تعتمد على الدالة tell لتعرف مركز القراءة الحالي. >>> file.seek(66) >>> file.tell() 66 تقبل الدّالة readline مُعاملا لتحديد عدد البايتات المرغوب قراءتها (تماما مثل شقيقتها read). >>> file.seek(0) # للعودة إلى بداية الملف >>> file.readline(8) 'Academy:' الدالة readlines تُعتبر الدالة readlines طريقة أخرى لقراءة الملف، وتُرجع قائمة بجميع الأسطر التي يحتويها الملف: >>> file = open('file.txt', 'r') >>> file.readlines() ['Academy: https://academy.hsoub.com/\n', 'Freelance: https://academy.hsoub.com/freelance\n', 'Entrepreneurship: https://academy.hsoub.com/entrepreneurship\n', 'Design: https://academy.hsoub.com/design\n', 'Apps: https://academy.hsoub.com/apps\n', 'Certificates: https://academy.hsoub.com/certificates\n', 'Devops: https://academy.hsoub.com/devops\n', 'Marketing: https://academy.hsoub.com/marketing'] يُمكنك مثلا حساب عدد أسطر ملفّ ما بحساب عدد عناصر القائمة بالدالة len: >>> file = open('file.txt', 'r') >>> len(file.readlines()) 8 إغلاق الملف رغم أنّ عمليّة إغلاق الملف ليست ضرورية لحفظ التّغييرات، إلّا أنّ القيام بالأمر منصوح به بشدّة، لذلك يجب عليك أن تتأكّد من أنّ الملفّ قد أُغلق بعد انتهائك من إجراء العمليّات على الملف. لقد تعرّفنا من قبل على كيفيّة إغلاق الملف: >>> file = open('file.txt') >>> file.close() للتحقق من أنّ الملف مُغلق يُمكن أن تستعين بالجملة closed والتي تُرجع قيمة منطقية (إما True أو False). إذا كان الملف مُغلقا فسترجع الجملة True أما إذا لم يكن مغلقا فستُرجع الجملة False. >>> file = open('file.txt') # فتح الملف >>> file.closed # التحقق من أنّ الملف مُغلق False >>> file.close() # إغلاق الملف >>> file.closed # التحقق من جديد True يُمكن كذلك الاستعانة بالجملة with as لفتح ملف والقيام بعمليات ثم إغلاقه آليا. with open('file.txt', 'r') as file: file.read() الملف يُغلق ذاتيا بعد انتهاء الجزء المُزاحِ (ذو الإزاحة) من الشيفرة، ويُمكنك التأكّد من الأمر بالجملة closed. الدوال المساعدة بعد فتح الملف يُمكنك الحصول على معلومات حول الملف ببعض الدوال المبنية مُسبقا Built-in functions وهي كالتالي: closed: التحقق من ما إذا كان الملف مُغلقا أو لا (انظر الفصل السابق). name: الحصول على اسم الملفّ. >>> file = open('file.txt') >>> file.name 'file.txt' mode: الحصول على نمط الوصول. >>> file = open('file.txt', 'w+') >>> file.mode 'w+' تمارين تمرين 1 اكتب برنامجا للحصول على قيم من المُستخدم وأدخلها إلى ملف نصي. تمرين 2 صحّح الخطأ في ما يلي: file = open('file.txt', 'r') file.write('Hello World!') تمرين 3 صحّح الخطأ في ما يلي: file = open('file.txt', 'w') file.write('Hello World!') file.close() file.write('Hello!') تمرين 4 اكتب برنامج تسجيل الدخول في الدروس السابقة ليقبل ملفّا كقاعدة بيانات. (احفظ اسم المُستخدم وكلمة المرور في الملف عوضا عن الذاكرة.) المصادر المعتمدة التوثيق الرّسمي للغة بايثون، فصل الدوال المبنية مُسبقا، الدالة open كتاب Python Practice Book لكاتبه Anand Chitipothu التوثيق الرّسمي للغة بايثون، فصل السلاسل النّصيّة
  16. المُزخرفات من أعظم مميزات لغة بايثون، إذ تساعدك على بناء برنامجك باحترافية أكثر موفرة طريقة بسيطة لإضافة خاصيات جديدة للدالة. وهي ببساطة دوال تستطيع أن تعدل على دوال أخرى. تذكير ببعض المفاهيم الأساسية إذا لم تكن تعرف شيئا عن الدوال في لغة بايثون فيجب عليك العودة للدرس السابق الدوال في بايثون قبل أن تكمل قراءة هذا الدرس. تُعتبر الدوال في لغة بايثون كائنات من نوع الفئة الأولى أو 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 أكمل البرنامج الخاص بالمثال الثالث (إنشاء مُزخرف لتنفيذ دالة عند تحقق شرط مُعين فقط). تفاصيل التمرين موجودة بالمثال.
  17. إلى الآن، تعرفنا في هذه السلسلة من الدروس على أساسيات التعامل مع أنواع البيانات، المُتغيرات، الجمل الشرطية، وحلقات التكرار for و while. وقد أصبح لديك الآن المعلومات الكافية لبرمجة برامج مُتوسطة قد تحتوي على مئات الأسطر البرمجية، وقد حان الوقت لتتعرف على الدوال لكي تجعل مُهمة إعادة استخدام أجزاء من شيفرتك أكثر سهولة ومرونة، كما ستتعرف على بعض من الدوال المُعرفة مُسبقا في لغة بايثون. للتذكير: جميع الشّيفرات التّي تبدأ بعلامة <<< يجب أن تنفّذ على مفسر بايثون. الدوال الدّالة في البرمجة كاسم المتغيّر يُمكنك استدعاؤها عند الحاجة لكنّ المتغير لا يحمل سوى قيمة واحدة، أمّا الدّالة فتحمل شيفرة مستقلة وقد تكون هذه الشيفرة جملة شرطية أو جملة طباعة أو حلقة تكراريّة أو كل ما سبق. وتُستعمل الدوال أساسا لتجنب تكرار شيفرة عدة مرات، فمثلا لنقل بأنّك كتبت شيفرة للجمع بين عددين ثم بعد ذلك أردت أن تجمع عددين آخرين وهكذا، إذا حاولت تكرار الشيفرة سيكون الأمر مُتعبا جدا خاصة إذا كانت الشيفرة التي تكررها طويلة جدا، لذلك فلغات البرمجة توفر الدوال. لنعتبر بأنّ طباعة جملة "Hello" هي الشيفرة التّي تحملها الدّالة، انظر المثال التّالي: >>> def say_hello(): ... print "Hello" ... >>> say_hello() Hello أولا قمنا بإنشاء الدّالة say_hello في السّطر: def say_hello(): النّقاط الثّلاثة التّي تظهر بعد ذلك السّطر تعني بأن مُفسر بايثون ينتظر مدخلات أخرى، لذلك ندخل شيفرة الطّباعة (تذكر بأن هذه هي الشيفرة الأساسيّة) بعد مساحة بيضاء (أربع مسافات). بعد إنشاء الدّالة سيرجع المفسّر إلى حالته الطّبيعية، بدون أي مُخرجات، وذلك لأنّنا يجب أن نستدعي الدّالة لكي تُنفّذَ الشّيفرة التّي بداخلها، والاستدعاء يكون بكتابة اسم الدّالة مع قوسين: >>> say_hello() ما رأيناه قبل قليل هو كيفية تعريف دالة، كيفية وضع شيفرة داخلها ثم كيفية استدعائها لتنفذ الشيفرة. يُمكن أن نجعل الدالة تُعيد قيمة دون تنفيذ أي شيفرة (رغم أنّها تُنفذ شيفرة الإرجاع)، انظر المثال التالي: >>> def x(): ... return 4 ... >>> x() 4 ما فعلناه هو أنّنا قُمنا بجعل الدالة قيمة عوضا عن أن تُنفذ شيفرة ما، أي أنّنا استخدمنا الدالة x كمُتغير عادي يحمل قيمة مُحدّدة. قد تسأل نفسك، ما فائدة الأمر؟. الأمر مُفيد جدا إذا كان برنامجك كبيرا، بحيث يُمكنك استعمال الدوال كقيمة مُخرجة بعد عدة عمليات. مثلا، إذا كان لديك برنامج يطلب من المُستخدم مُدخلا (كاسمه أو كلمة مروره) وتُريد التحقق من مُدخله وإجراء عمليات عليه، فاستعمال المُتغيرات هنا ليست فكرة جيدة وذلك لأنّ الشيفرة ستكون صعبة التعديل. عوضا عن ذلك يُمكنك أن تقوم بالعمليات على المدخل داخل دالة ثم بعد ذلك تجعل الدالة تحمل القيمة التي ترغب بها في النهاية. دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن ملاحظة حول تسمية الدوال رغم أنك تستطيع تسمية الدوال بأحرف صغيرة وكبيرة معا، من المُفضل تسمية الدالة بأحرف صغيرة والفصل بين كل كلمة والأخرى بمحرف "_". المعاملات Parameters من مُميزات الدوال أنّك تستطيع أن تُعطيها مُعاملات عند تعريفها، وأن تُعطي القيم لهذه المُعاملات إما عند تعريف الدالة (المعاملات الافتراضية) أو لاحقا. لتفهم أكثر، انظر المثال التالي: >>> def say_hello(name): ... print 'Hello', name ... >>> say_hello('Abdelhadi') Hello Abdelhadi >>> say_hello('Hsoub Academy') Hello Hsoub Academy كما ترى لقد قُمنا بتعريف الدالة say_hello مع مُعامل name ووضعنا داخلها جملة طباعة لتطبع Hello متبوعا بقيمة العامل name بعدها قُمنا باستدعاء الدالة وأسندنا للعامل name قيمتين مُختلفتين، وقد كانت النتائج مُختلفة، يُمكنك أن تستدعي نفس الدالة مع معاملات مُختلفة لعدد غير محدود من المرات. يُمكنك كذلك أن تقوم بتعريف دالة مع أكثر من معامل، إليك مثالا على دالة لإعادة مجموع عددين : >>> def add_numbers(n1, n2): ... return n1 + n2 ... >>> add_numbers(3,4) 7 إذا قُمنا بتعريف دالة مع معامل فلا بد أن نقوم بتعيين قيمة العامل عند الاستدعاء وإلا فسيرجع مُفسر Python خطأ كالتّالي: >>> say_hello() TypeError: say_hello() takes exactly 1 argument (0 given) الأمر نفسه يحدث إذا عينت عددا أكثر من العوامل المُفترض، مثلا أن تعطي للدالة say_hello مُعاملين أو أكثر. يُمكنك أن تقوم بوضع قيمة افتراضية لمُعامل في سطر تعريف الدالة: >>> def num(n1, n2 = 10): ... return n1 + n2 ... >>> num(3) 13 يُمكن أن تستعمل هذه الطريقة لعرض خطأ للمُستخدم إذا نسي إدخال المُعاملات. >>> def say_hello(name = '| Error! Check the line that calls the function'): ... print 'Hello', name ... >>> say_hello() Hello | Error! Check the line that calls the function يُمكنك كذلك أن تقوم بعدة عمليات مع الدوال، وإليك قائمة ببعض هذه العمليات: تعريف دالة داخل دالة أخرى بما أن الدوال عبارة عن أجزاء من الشيفرة فتستطيع أن تقوم بإنشاء دالة أخرى أو عدد من الدوال داخل الدالة الواحدة، انظر المثال التالي: >>> def say_hello(name): ... def hello(): ... return 'Hello ' ... print hello() + name ... >>> say_hello('Abdelhadi') Hello Abdelhadi كما ترى أعلاه القيمة Hello ما هي إلا القيمة التي أرجعتها الدالة hello المعرفة بداخل الدالة say_hello. استدعاء دالة داخل دالة أخرى هذا الأمر بسيط، فقط استدع الدالة داخل دالة أخرى وستعمل كلتا الدالتان عند استدعاء الدالة الأم: >>> def say_hello(name): ... print 'Hello', name ... >>> def print_hello(): ... say_hello('Abdelhadi') ... >>> print_hello() Hello Abdelhadi في المثال أعلاه قُمنا باستدعاء الدالة say_hello من داخل الدالة print_hello. إسناد دالة لمتغير تستطيع أن تُنشئ دالة وتقوم بإسنادها لمُتغير بالطّريقة التالية: >>> def say_hello(name): ... print 'Hello', name ... >>> s = say_hello >>>> s('Abdelhadi') Hello Abdelhadi إسناد دالة كمعامل لدالة أخرى قلنا مُسبقا بأنّه يمكن للدالة أن تتصرف كمتغير وذلك عندما نجعلها ترجع قيمة بالجملة return. وهذا يعني بأنّنا نستطيع أن نسند للدالة دالة أخرى. >>> def say_hello(name): ... print 'Hello', name ... >>> def get_name(): ... return 'Abdelhadi' ... >>> say_hello(get_name()) Hello Abdelhadi إرجاع دالة داخل دالة أخرى يُمكنك أيضا أن تستعمل الجملة return لإرجاع دالة داخل دالة أخرى عوضا عن إرجاع قيمة ما، انظر المثال: >>> def get_hello(): ... return 'Hello Abdelhadi' ... >>> def say_hello(): ... return get_hello() ... >>> print say_hello() Hello Abdelhadi ستلاحظ بأنّ الدالة say_hello تحمل قيمة الدالة get_hello، وذلك لأنّنا أرجعنا هذه الأخيرة داخل الدالة say_hello. إسناد عدد لا نهائي من المعاملات لدالة ما لنقل بأنّك تريد أن تقوم بإنشاء دالة تطبع جميع المُحتويات (المُعاملات) التي تُدخلها دون الحاجة إلى تحديد كل معامل على حدة. يُمكنك أن تقوم بالأمر بما يُسمى مُعامل طول المُتغير أو Variable-length argument وذلك كالتالي: >>> def print_arguments(*args): ... print args ... >>> print_arguments(2, 4, 8, 'Abdelhadi', 'Hsoub Academy') (2, 4, 8, 'Abdelhadi', 'Hsoub Academy') المُتغير args عبارة عن صف (Tuple) من القيم، وبالتالي فإنك تستطيع أن تتعامل معه بحلقة For. يُمكن أن تستخدم أي اسم نريده لهذا المعامل، وعموما يعتبر الاسم args* تقليدا بين مُبرمجي بايثون. لاحظ أن الاختلاف بين المعامل العادي والمعامل في المثال أعلاه هو النجمة في البداية. تستطيع أن تجعل المُعاملات قاموسا عوضا عن صف بحيث تُرجع الدالة القاموس الذي يحتوي على اسم المعامل وقيمته، وذلك كالآتي: >>> def print_keyword_args(**kwargs): ... print kwargs ... >>> print_keyword_args(name = 'Abdelhadi', website = 'Hsoub Academy', n = 3) {'website': 'Hsoub Academy', 'name': 'Abdelhadi', 'n': 3} لاحظ بأنّ اسم المُعامل يبدأ بنجمتين هذه المرة، وهذا النوع من المُعاملات يسمى مُعاملات الكلمة المفتاحية Keyword Arguments لأنها ترجع قاموسا مفاتيحه هي أسماء المعاملات التي تختارها أنت وقيم المفاتيح هي قيم المعاملات. المتغيرات المحلية والمتغيرات العامة Local variables and Global variables المتغيرات المحلية هي المتغيرات التي نقوم بإنشائها داخل دالة، أما المتغيرات العامة فهي التي ننشئها خارج الدالة. يُمكنك أن تصل إلى المتغيرات العامة في أي مكان من برنامجك، على خلاف المُتغيرات المحلية التي ستتمكن من الوصول إليها فقط داخل الدالة، انظر المثال التالي: >>> def say_hello(): ... name = 'Abdelhadi' ... print 'Hello', name ... >>> say_hello() Hello Abdelhadi >>> print name Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'name' is not defined قُمنا بتعريف المُتغير name داخل الدالة say_hello وبعدها حاولنا الوصول إليه خارج الدالة، ما أدى إلى خطأ مفاده بأن مُفسر بايثون لم يتعرف على المُتغير. يُمكن أن نجعل المتغير name عامًا بإضافة الكلمة global قبله (عليك أن تقوم بهذا قبل إسناد قيمة للمُتغير)، انظر المثال التالي: >>> def say_hello(): ... global name ... name = 'Abdelhadi' ... >>> say_hello() >>> print name Abdelhadi نُلاحظ بأنّنا استطعنا الوصول إلى المُتغيّر name خارج الدالة التي عُرّفَ بها. تطبيق سنضرب مثالا بالبرنامج الذي أنشأناه في درس التعابير الشرطية والإزاحات. print 'Hello User, this is a basic sign up/login Program' username = raw_input('Enter your username please: ') password = raw_input('Enter the your password please: ') password_verification = raw_input('Verify password: ') if password == password_verification: print 'You have Successfully Signed up! \n' username_sign_in = raw_input('Enter your username please: ') password_sign_in = raw_input('Enter your password please: ') if username_sign_in == username and password_sign_in == password: print 'You have Successfully Signed in!' else: print 'username or password do not match! Please try again!' else: print 'The password and the password verification do not match! Please try again' سنركز على شيفرة التحقق من المُستخدم وكلمة المرور: if username_sign_in == username and password_sign_in == password: print 'You have Successfully Signed in!' else: print 'username or password do not match! Please try again!' إذا أردنا أن نستخدمها لأكثر من مرة في برنامجنا فعلينا أن نضعها داخل دالة. def user_logged_in?(username_sign_in, username, password_sign_in, password): if username_sign_in == username and password_sign_in == password: return True else: return False يُمكنك استخدام الدالة في أي مكان من الشيفرة: def is_user_logged_in(username_sign_in, username, password_sign_in, password): if username_sign_in == username and password_sign_in == password: return True else: return False new_password = raw_input('Enter new username: ') new_username = raw_input('Enter new password: ') print 'Ok!' password = raw_input('Hello again, Enter your username: ') username = raw_input('Enter username\'s password: ') print is_user_logged_in(username, new_username, password, new_password) الدالة is_user_logged_in تقوم بمُقارنة كلمتي المرور واسمي المُستخدم فإن كانا متطابقين فستصبح قيمة الدالة True أما إن لم تتوافق القيم فإنّ الدالة تحمل القيمة False ويُمكنك الآن استخدام الدالة مع الجمل الشرطية في أي مكان من برنامجك دون الاضطرار لإعادة كتابة شيفرة التحقق كل مرة. كما أنّ التعديل على الشيفرة بسيط جدا، فالتّعديل من مكان واحد (الدالة) يكفي يطبّق على جميع الأماكن الأخرى (أمكنة استدعاء الدالة). الدوال المعرفة مسبقا تُوفر بايثون دوالا مُعرفة مُسبقا، وقد تعاملنا مع كثير منها في الدروس السابقة، وإليك بعض الدوال المُفيدة التي توجد مُسبقا بلغة بايثون. print: دالة الطّباعة التي استخدمناها أكثر من مرة لطباعة القيم في سابق الدروس. Int: دالة تحويل القيم إلى عدد صحيح، مثال: >>> int(5.5) 5 str: دالة تحويل القيم إلى سلسلة نصيّة String: >>> str(True) 'True' وتُستعمل كثيرا لجمع سلسلة نصية بقيمة من نوع آخر بالعامل + (إذا لم تستخدم هذه الدالة فسيعرض لك مُفسر بايثون خطأ يُفيد بأنّك لا تستطيع جمع قيمتين من نوعين مُختلفين): >>> print 'ABC' + str(122) + str(False) ABC122False abs: دالة للحصول على القيمة المُطلقة Absolute Value لعدد ما (إذا كان موجبا فهو العدد نفسه، وإذا كان سالبا فمُقابله): >>> abs(3) 3 >>> abs(-3) 3 len: دالة لقياس عدد عناصر قائمة أو عدد أحرف سلسلة نصية. >>> a = ['Abdelhadi', 'Dyouri', 'Academy', 'Hsoub'] >>> b = 'Hello' >>> len(a) 4 >>> len(b) 5 min: دالة لتحديد أقل قيمة بين قيمتين أو أكثر. >>> min(4, 5, 7, 88, 3, 2) 2 max: دالة لتحديد أقل قيمة بين قيمتين أو أكثر. >>> max(4, 5, 7, 88, 3, 2) 88 range: لتوليد قائمة بأعداد بين قيمة المعامل الأول وقيمة المُعامل الثاني: >>> range(0, 11) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> range(0, 11, 3) [0, 3, 6, 9] raw_input: دالة للحصول على مُدخل نصي من المُستخدم، يُمكنك أن تسند هذا المُدخل إلى مُتغير. >>> def say_hello(name): ... print 'Hello', name ... >>> say_hello(raw_input()) Abdelhadi Hello Abdelhadi كما قُلنا سابقا، فإنك تستطيع أن تقوم بإسناد دالة كمعامل لدالة أخرى، وهذا ما فعلناه في المثال أعلاه، إذ مررنا الدالة raw_input ليكون معاملا للدالة say_hello. تمارين تمرين 1 اكتب برنامجا للحصول على قيمة من المُستخدم وتحويلها إلى قيمة عددية. تمرين 2 اكتب دالة لطباعة جملة لعدد من المرات، واترك للمُستخدم حرية اختيار عدد مرات التكرار (مُساعدة: استعمل الدالة raw_input للحصول على قيمة من المُستخدم) تمرين 3 اكتب دالة للجمع بين عددين يُدخلهما المُستخدم. تمرين 4 بَرمِجْ آلة حاسبة بسيطة تقوم بالجمع، الطّرح، الضرب والقسمة. إليك طريقة العمل: احصل على قيمة العدد الأول من المُستخدم. احصل على قيمة العدد الثاني. حول القيمتين النصيتين إلى قيمتين عدديتين. احصل على العامل الرياضي (*، /، - أو +) من المُستخدم. استخدم الجمل الشرطية لإجراء العملية المُناسبة (إذا كان المُدخل + فقم بالجمع). ترجمة -وبتصرف- من الكتاب Python Practice Book لكاتبه Anand Chitipothu.
  18. سنتطرق في هذا الدّرس إلى كيفية تطبيق حماية أساسيّة لخادوم Redis. على الرغم من هذا، عليك تذكر بأن Redis قد صُمّم أساسا للاستخدام من طرف مستخدمين موثوقين على بيئة موثوقة، بدون أي مميزات أمنيّة خاصة به. لفهم هذه النّقطة أكثر إليك اقتباسا من الموقع الرسمي لـredis: بشكل عام، Redis ليس مُحسّنا لحماية قصوى بل لأداء عالي وبساطة فائقة. الأداء والبساطة بلا حماية بمثابة وصفة لكارثة. حتى مميّزات الحماية القليلة لدى Redis ليست ممتازة، وتشمل كلمة مرور بسيطة غير مُشفّرة، إعادة تسميّة الأوامر وتعطيلها. وتفتقر إلى نظام حقيقي للتحكم بالولوج. على الرغم من هذا، فإن ضبط هذه الخصائص الأمنيّة يبقى خطوة كبيرة إلى الأمام وأفضل من ترك قاعدة بياناتك بدون حماية. ستقرأ في هذا الدّرس كيفيّة ضبط الخصائص الأمنيّة القليلة التي يمتلكها Redis، وبعض الخصائص الأمنية الأخرى للنظام ما سيزيد من نسبة الحماية لخادوم Redis مُستقل على Ubuntu 14.04. ننوّه إلى أن هذا الدرس لا يتحدث عن حالات وجود كل من خادوم Redis وتطبيقات العملاء على استضافات مختلفة أو مراكز بيانات مختلفة. أو الحالات التي يجب على الاتصال بـ Redis أن يقطع شبكات غير آمنة أو غير موثوقة تتطلب نوعا مختلفا من الإعداد، مثل ضبط SSL proxy أو VPN بين خواديم Redis ، إضافة إلى الخطوات المذكورة هنا. المتطلبات ستحتاج في هذا الدّرس: خادوم Ubuntu مع مستخدم sudo مُضَاف، انظر درس الإعداد الابتدائي لخادوم أوبنتو 14.04 جدار ناري iptables معدّ باستخدام الدرس كيف تنفذ نموذجا للجدار الناري باستعمال Iptables إلى غاية خطوة "تحديث nameservers" (إذا لم تقم بإعداد جزء الخادوم nameserver فالـAPT لن يعمل). Redis مُثبت وقيد التنفيذ باستعمال الخطوات في الموضحة في الدرس كيفية تنصيب واستخدام Redis على أوبنتو. الخطوة الأولى، التأكد من أن Redis قيد التشغيل أولاً ادخل إلى الخادوم باستعمال SSH: ssh username@server-ip-address username: اسم المستخدم server-ip-address: عنوان IP الخاص بالخادوم للتأكد من أنّ Redis قيد التنفيذ، استعمل سطر الأوامر الخاص ب Redis .يُستعمل الأمر redis-cli للوصول إلى سطر أوامر Redis. redis-cli إذا سبق وأن أعددت كلمة مرور لـ Redis، فيجب عليك الاستيثاق بالأمر auth بعد الاتّصال: auth كلمة_المرور المخرج سيكون كلمة OK اختبر خادوم قاعدة البيانات: ping الرّد: PONG اُخرج: quit الخطوة الثانية، حماية الخادوم بـاستخدام Iptables إذا اتّبعت المُتطلّبات من أجل ضبط Iptables، فلك كامل الحريّة في تخطّي هذه الخطوة، أو يُمكنك القيام بذلك الآن. Redis مجرّد تطبيق يُنفّذ على خادومك، ولأنه لا يملك أي خصائص أمنيّة خاصة، فإنّ أول خطوة لحمايته حقيقة تكمن في حماية الخادوم الذي يُشغل منه. في حالة خادوم موجّه للعامة كخادومك Ubuntu 14.04، فإن ضبط جدار ناريّ كما هو مبيّن في درس Iptables يُعتبر الخطوة الأولى لذلك. اتّبع ذلك الرّابط واضبط جدارا ناريّا الآن. إذا طبّقت أحكام الجدار النّاري باستعمال ذلك الدّرس، فلن تحتاج لإضافة قاعدة أخرى لـ Redis، افتراضيّاً، إن أي مرور traffic يُمنَع إلّا عند السّماح له صراحةً. وبما أن خادوم Redis افتراضيّا يستمع على واجهة الاسترجاع (loopback (127.0.0.1 أو localhost، فلا يجب القلق حول المرور عبر المنفذ الافتراضي. إذا كنت تحتاج للسّماح لعنوان IP للوصول خصّيصا لـ Redis، يُمكنك التحقق من العنوان الذي يستمع منه Redis، وعلى أي منفذ باستخدام أمر grep لمُخرجات الأمر netstat. العمود الرّابع 127.0.0.1:6379 يطلعنا على عنوان IP والمنفذ المرتبطين بـ Redis: sudo netstat -plunt | grep -i redis المخرجات: tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 8562/redis-server 1 تأكّد من أن هذا العنوان مسموح له في الجدار النّاري. للمزيد من المعلومات عن كيفيّة إضافة الأحكام، اطّلع رجاء على درس أساسيّات Iptables. الخطوة الثالثة، الربط إلى المضيف المحلي localhost افتراضيًّا، يُمكن الوصول إلى خادوم Redis من المضيف المحليّ، على أي حال، إذا اتّبعت درس ضبط Redis على خادوم متبوع/سيّد فقد حدّثت ملفّ الإعدادات للسّماح للاتّصالات من أي مكان. وهذا ليس آمنا كالربط إلى المُضيف المحليّ. افتح ملفّ إعدادات Redis للتّعديل: sudo nano /etc/redis/redis.conf ابحث عن هذا السّطر وتأكّد من أنه ليس على شكل تعليق (احذف الرّمز # إذا كان موجودا): bind 127.0.0.1 سنستمرّ في استعمال هذا الملفّ، لذلك أبقه مفتوحا الآن. الخطوة الرابعة، ضبط كلمة مرور لـ Redis إذا ثبتت Redis باتّباع درس كيف تضبط خواديم Redis متعددة على أوبنتو 14.04 فيجب عليك أن تكون قد ضبطت كلمة مرور مُسبقا، يُمكنك أن تضع كلمة مرور أكثر أمانا باتّباع هذا القسم الآن، وهذه الخطوات تعرض كيفية وضع كلمة مرور لخادوم قاعدة البيانات. ضبط كلمة مرور لـ Redis يفعّل إحدى ميزتي الحماية المُضمّنة في Redis، وهو الأمر auth، الذي يطلُب من العملاء الاستيثاق للوصول إلى قاعدة البيانات. تُضبَطُ كلمة المرور مُباشرة في ملف الإعدادات etc/redis/redis.conf/ الذي يجب أن تكون قد فتحته منذ الخطوة الماضيّة. اذهب عن قسم SECURITY وابحث عن سطر مُعلّق بالرمز # كالتّالي: # requirepass foobared أزل التّعليق عن السّطر بحذف الرمز #، وغيّر foobared إلى كلمة مرور قويّة وطويلة. عوضاً عن الإتيان بكلمة مرور من عندك، يمكنك استعمال أداة مثل apg و pwgen لتوليد واحدة. إذا لم تكن ترغب بتثبيت أداة فقط لتوليد كلمة مرور، يُمكنك استخدام السطر التّالي. لتوليد كلمة مرور مُختلفة عن هذا المثال غير القيمة بداخل علامتي التنصيص: echo "hsoub-academy" | sha256sum المُخرج سيبدو كالتّالي: e118c2b8fa805fbd0a6af458ab7df9fea14881b87c2e793b4a2192cda30bf8d5 لكنّ كلمة المرور المولّدة لن تكون منطوقة، وهي قويّة وطويلة وهو تماما المطلوب لحماية Redis. بعد نسخ ولصق مُخرج هذا الأمر كقيمة جديدة لـ requirepass، يجب أن تكون كالتّالي: requirepass 960c3dac4fa81b4204779fd16ad7c954f95942876b9c4fb1a255667a9dbe389d إذا كنت تُفضّلُ كلمة مرور أقصر، استخدم مُخرج الأمر التالي عوضا عنها. غيّر القيمة بين علامتي التنصيص لكي لا تولّد نفس كلمة المرور: echo "hsoub-academy" | sha1sum المخرج سيكون أقصر هذه المرّة: 773a12cfe8616ff740258f8843c567f32227ac0f بعد ضبط كلمة المرور، احفظ الملفّ وأعد تشغيل Redis : sudo service redis-server restart لاختبار عمل كلمة المرور، حاول الوصول إلى سطر أوامر Redis : redis-cli السطر التالي يعرض سلسلة من الأوامر المُستعملة لاختبار ما إذا كانت كلمة المرور تعمل أو لا، الأمر الأول يُحاول إعطاء قيمة لمفتاح قبل القيام بالاستيثاق: set key1 10 الأمر لن يعمل، لذلك فإنك سترى خطأ في المخرجات: (error) NOAUTH Authentication required. الأمر الثاني يقوم بالاستيثاق بكلمة المرور التي وضعناها في ملفّ إعدادات Redis. auth your_redis_password سيُوافق Redis في الحال: OK بعد هذا، تنجح إعادة تنفيذ الأمر السّابق. set key1 10 المُخرج: OK الأمر get key1 يطلب من Redis قيمة المفتاح الجديد: get key1 المُخرج: “10” الأمر الأخير يقوم بإنهاء سطر الأوامر redis-cli. ويمكنك أيضاً استخدام exit: quit تالياّ، سننظر إلى إعادة تسميّة أوامر Redis. الخطوة الخامسة، إعادة تسمية الأوامر الخطرة الخاصيّة الأمنيّة الأخرى المبنيّة داخل Redis تُخوّل لك إعادة تسميّة أو تعطيل أوامر معيّنة والتّي تُعتبر أوامر خطيرة. عند تشغيل مستخدمين غير مُخولين لأوامر معينّة فقد تُستخدم هذه الأوامر لإعادة ضبط، أو تدمير أو حذف بياناتك. ومثل كلمة مرور الاستيثاق،فإن إعادة تسمية الأوامر أو تعطيلها تتم في نفس قسم SECURITY من الملفّ etc/redis/redis.conf/. بعض الأوامر المعروف أنها خطيرة هي: FLUSHDB, FLUSHALL, KEYS, PEXPIRE, DEL, CONFIG, SHUTDOWN, BGREWRITEAOF, BGSAVE, SAVE, SPOP, SREM, RENAME و DEBUG وهذه القائمة ليست شاملة، لكنّ إعادة تسميّتها أو تعطيلها كلّها يُعتبر نقطة بداية جيّدة. تعطيل أو إعادة تسميّة أمر يعتمد على مدى استعمالك للأمر، فمثلاً إذا كنت تعلم أنّك لن تستعمل أبدا أمرا يُمكن أن يُستَغلّ سلبيّاً، فمن المستحسن أن تُعطّله، عدا ذلك فمن الأفضل إعادة التّسميّة. لتشغيل أو تعطيل أمر ما، افتح ملفّ الإعدادات للتحرير مرة أخرى: sudo nano /etc/redis/redis.conf هذه بعض الأمثلة فقط. ويجب عليك أن تعيد تسميّة أو تعطّل الأوامر بما يُناسبك. يُمكنك التحقق من الأوامر بنفسك وتحديد إمكانيّة سوء استعملها على redis.io/commands. لتعطيل أو إيقاف أمر ما، فقط أعد تسميّتها إلى قيمة فارغة كما هو مبيّن أسفله: # It is also possible to completely kill a command by renaming it into # an empty string: # rename-command FLUSHDB "" rename-command FLUSHALL "" rename-command DEBUG "" وعند إعادة تسميّة أمر ما، عيّن اسما آخر كقيمة، كما في الأمثلة أسفله. الأوامر المعاد تسميّتها يجب أن تكون صعبة التخمين، وفي نفس الوقت سهلة التذكر بالنسبة لك.ولا تجعلها صعبة عليك. rename-command CONFIG "" rename-command SHUTDOWN SHUTDOWN_MENOT rename-command CONFIG ASC12_CONFIG بعد إعادة التّسميّة، طبّق التعديلات بإعادة تشغيل Redis: sudo service redis-server restart لاختبار الأمر الجديد، ادخل إلى سطر أوامر Redis: redis-cli بعد ذلك، على افتراض بأنّك قمت بإعادة تسميّة الأمر CONFIG إلى ASC12_CONFIG، المُخرج التّالي يوضح كيفيّة التأكد من أنّ الأمر الجديد يعمل. بعد الاستيثاق: auth your_redis_password المُخرج: OK يجب أن تكون مُحاولة استخدام الأمر config باسمه الأصلي فاشلة، لأنّنا بالطبع قمنا بإعادة تسميّتها: config get requirepass المُخرج": (error) ERR unknown command 'config' مناداة الأمر باسمه الجديد سيعمل (وهو غير حسّاس لحالة الأحرف): asc12_config get requirepass المُخرج: 1) "requirepass" 2) "your_redis_password" أخيرا، يمكنك الخروج من redis-cli: exit ملاحظة: إذا كنت تستخدم مسبقا سطر الأوامر Redis ثم أعدت تشغيل Redis، سيتوجب عليك إعادة الاستيثاق. إن لم تقوم بذلك، ستحصل على هذا الخطأ عند كتابة أمر ما: NOAUTH Authentication required. على الرغم من إعادة تسميّتك للأوامر، فهناك جملة تحذيريّة في آخر قسم SECURITY على ملفّ etc/redis/redis.conf/ تقول: هذا يعني أنّه إذا لم يكن الأمر المعاد تسميّته داخل ملفّ AOF، أو إذا كان موجودا لكنّ ملفّ AOF لم يُحَلْ إلى التّوابع، فلا يجب أن يكون هناك أي مُشكلة. لذلك تذكّر عند محاولة تغيير أسماء الأوامر، أفضل وقت لإعادة تسميّة أمر هو عند عدم استخدام AOF، أو مباشرة بعد التثبيت أي قبل نشر التطبيق المُستعمِل لـ Redis. عندما تستخدم AOF وعند التّعامل مع نظام تابع-متبوع، ضع في بالك هذه الإجابة من صفحة المسائل في المشروع على Github. الاقتباس التّالي هو جواب على سؤال الكاتب: لذلك فإن أفضل طريقة للتعامل مع إعادة التسمية في حالات كهذه هي أن تتأكّد من أنّ إعادة تسميّة الأوامر مطبّقة في جميع الخوادم في أنظمة تابع-متبوع. الخطوة السادسة، ضبط مجلد البيانات وصلاحيات المستخدم للملفات في هذه الخطوة، سوف نعرض بعض تعديلات المالك والصلاحيّات التّي يُمكنك القيّام لها لزيّادة حماية Redis. يتمثّل هذا في التأكد من أنّ المُستخدم الذي يحتاج إلى الوصول إلى Redis فقط يملك صلاحيّات قراءة البيانات. هذا المُستخدم افتراضيّا هو المُستخدم redis. يُمكنك التحقق من هذا باستخدام أداة grep مع الأمر ls لرؤية معلومات عن المُجلّد الأب لمجلّد بيانات Redis. الأمر والمُخرجات أسفله. ls -l /var/lib | grep redis المُخرج: drwxr-xr-x 2 redis redis 4096 Aug 6 09:32 redis يُمكنك ملاحظة بأن مجلّد بيانات Redis مملوك من طرف المُستخدم Redis، مع وصول ثانوي للمجموعة redis. وهذا أمر جيّد. الأمر السيئ هو صلاحيّات الوصول إلى المُجلّد، أي 755. للتأكد من أن المُستخدم redis هو فقط من يمتلك حقّ الوصول إلى المجلد ومُحتوياته، غيّر الصلاحية إلى 700: sudo chmod 700 /var/lib/redis الصلاحيّة الأخرى التّي عليك تغييرها هي الخاصّة بملفّ إعدادات Redis. يمتلك افتراضيّا صلاحيّة 644 ومملوك للمُستخدم الجذر root، مع مالك ثانوي يتمثّل في مجموعة الجذر root group: ls -l /etc/redis/redis.conf المُخرج: -rw-r--r-- 1 root root 30176 Jan 14 2014 /etc/redis/redis.conf هذه الصّلاحيّة (644) تعني مقروء من الكل، والتي تعتبر فكرة سيّئة لأن ذلك الملف يحتوي على كلمة المرور غير المُشفرة التّي وضعناها في الخطوة الرّابعة. نحتاج إلى تغيير المالك والصّلاحيّات. يجبُ أن تكون مملوكة للمُستخدم redis مع مجموعة الجذر كمالك ثانوي، لفعل ذلك، شغّل الأمر التّالي: sudo chown redis:root /etc/redis/redis.conf بعد ذلك غيّر المالك لكي يتمكّن مالك الملفّ فقط من قراءته أو/و الكتابة عليه: sudo chmod 600 /etc/redis/redis.conf يُمكنك التحقّق من المالك الجديد والصّلاحيّات باستخدام الأمر: ls -l /etc/redis/redis.conf المُخرج: total 40 -rw------- 1 redis root 29716 Sep 22 18:32 /etc/redis/redis.conf في الأخير، أعد تشغيل Redis: sudo service redis-server restart خاتمة تذكّر بأنه عند دخول أي شخص إلى خادومك، فمن السّهل التحايل على خصائص Redis الأمنيّة التي قمنا بضبطها. لذلك فإنّ أكثر خاصيّة أمنيّة هي التي تجعل من تجاوز هذا الحاجز صعبا قدر المُستطاع. أي خاصيّة الجدار النّاري. للتّقدّم بحماية خادومك إلى المستوى التّالي، فعليك ضبط نظام كشف تطفّل مثل OSSEC. إذا كنت تريد حماية تواصل Redis عبر شبكات غير آمنة فعليك توظيف SSL proxy، كما هو منصوح به من مطوّري Redis على دليل حمايةRedis الرسمي. وضبط SSL proxy لحماية تواصل Redis فهو موضوع آخر. لم نتطرّق إلى قائمة شاملة من أوامر Redis بقسم إعادة التّسميّة، لكنّك تستطيع إلقاء نظرة على مجموعة الأوامر بنفسك وتحديد إمكانيّة سوء استعملها على redis.io/commands. ترجمة -بتصرّف- للمقال How To Secure Your Redis Installation on Ubuntu 14.04 لصاحبه finid.
  19. Redis عبارة عن نظام تخزين مؤقّت cache بنمط مفتاح-قيمة key-value مفتوح المصدر. إن كنت ترغب في استخدام Redis على بيئة الإنتاج الخاصة بك، فإن نسخ بياناتك على خادومين على الأقل يُعتبر من بين أفضل الممارسات (best practices)، تتيح الوفرة الاسترداد في حالة فشل البيئة، ما يُعتبر مهمّا عند نمو قاعدة مستخدمي تطبيقك. بنهاية هذا الدرس، سنضبط خادومي Redis كالتالي: خادوم Redis للمتبوع/السيد (master server) خادوم Redis للتّابع (slave server) سنشرح كذلك كيفيّة الانتقال إلى خادوم التّابع وضبطه كسيّدِِ مؤقّت. لك كامل الحريّة في ضبط أكثر من خادوم واحد خاص بالتابع. هذا الدرس يركز على ضبط عنقود تابع ومتبوع خاص بـredis، لتعلم المزيد عن Redis بشكل عام واستخدامه الأساسي كقاعدة بيانات، ألق نظرة على هذا الدرس. المتطلبات في حين سيعمل هذا على إصدارات أقدم وتوزيعات لينكس أخرى، لكن ننصح باستعمال نظام أوبنتو 14.04. سنعتمد على: نظام أوبنتو Ubuntu 14.04 LTS. خادومين، بأي حجم تريد، واحد متبوع وآخر أو أكثر للتّوابع. ادخل إلى خواديمك عبر SSH مع مستخدم غير جدري مع صلاحيّات sudo كما هو مبيّن في الإعداد الابتدائي لخادوم أوبنتو 14.04 . الخطوة الأولى: تثبيت Redis سنبدأ مع الخادوم الذي سيستضيف المتبوع/السّيّد، وأول خطوة هي تثبيت Redis. أولا نحتاج إلى إضافة مستودع Redis الخاص بـ Chris Lea (كما جرت عليه العادة، توخ الحذر عند إضافة مستودعات طرف ثالث، نستخدم هذا المستودع لأن صاحبه حسنُ السمعة): sudo add-apt-repository ppa:chris-lea/redis-server اضغط ENTER لقبول المستودع. شغل الأمر التالي لتحديث الحزم: sudo apt-get update ثبت خادوم Redis: sudo apt-get update تأكد أنّ Redis مُشغل وقيد التنفيذ: redis-benchmark -q -n 1000 -c 10 -P 5 الأمر أعلاه يخبر بأننا نريد تشغيل الأمر redis-benchmark في وضع صامت، مع 1000 طلبات إجماليّة، 10 اتصالات موازية، و 5 طلبات أنابيب. للمزيد من المعلومات عن هذا الأمر، شغّل الأمر redis-benchmark –help على الطرفيّة لتظهر لك معلومات وأمثلة حول الأمر. بعد انتهاء العمليّة، يجب أن ترى مُخرجات تبدو كالتالي: PING_INLINE: 166666.67 requests per second PING_BULK: 249999.98 requests per second SET: 249999.98 requests per second GET: 499999.97 requests per second INCR: 333333.34 requests per second LPUSH: 499999.97 requests per second LPOP: 499999.97 requests per second SADD: 499999.97 requests per second SPOP: 499999.97 requests per second LPUSH (needed to benchmark LRANGE): 499999.97 requests per second LRANGE_100 (first 100 elements): 111111.12 requests per second LRANGE_300 (first 300 elements): 27777.78 requests per second LRANGE_500 (first 450 elements): 8333.33 requests per second LRANGE_600 (first 600 elements): 6369.43 requests per second MSET (10 keys): 142857.14 requests per second الخطوة الثانية: ضبط Redis المتبوع الآن بما أن Redis قيد التنفيذ على العنقود المتكون من الخادومين، يجب علينا تعديل ملفات الإعدادات. كما سنرى، هناك اختلافات طفيفة بين ضبط الخادوم المتبوع والتابع. لنبدأ أولا مع المتبوع master. افتح الملف etc/redis/redis.conf/ باستخدام محرر النصوص المُفضّل لديك: sudo nano /etc/redis/redis.conf وعدّل الأسطر التّالية: ضع قيمة معقولة لمؤقّت الإبقاء على قيد الحياة KeepAlive للـTCP: tcp-keepalive 60 اجعل الخادوم متاحا للجميع على الويب بجعل هذا السّطر كتعليق (أو بحذفه): #bind 127.0.0.1 بحكم طبيعة Redis وسرعاتها العاليّة قد ينفذ مهاجم هجوم القوة الغاشمة Brute force على كلمة المرور. لذلك ننصح إزالة تعليق سطر requirepass وإضافة كلمة مرور معقّدة (أو من الأفضل أن تكون جملة مرور معقدة): requirepass your_redis_master_password على حسب سيناريو استخدامك، قد ترغب في تعديل السّطر التالي أو لا. لغرض هذا الدرس ، نفترض أن حذف أي مفتاح key deletion ممنوع. قم بإزالة التعليق عن هذا السّطر واضبطه كالتالي: maxmemory-policy noeviction أخيرا، سنقوم بالتعديلات التاليّة، وهي مطلوبة للنسخ الاحتياطي للبيانات. أزل التعليق/أو اضبط هذه الأسطر كالتالي: appendonly yes appendfilename redis-staging-ao.aof احفظ التغييرات. أعد تشغيل خدمة Redis لإعادة تحميل تعديلات الضبط التي قمنا بها. sudo service redis-server restart إذا أردت، يُمكنك إضافة بعض المحتوى إلى قاعدة بيانات المتبوع من خلال اتباع قسم أوامر Redis في هذا الدرس. حتى نتمكن من معرفة كيفية نسخه إلى الخادوم التابع. الآن وبعد أن جهزنا خادوم المتبوع أو السيّد، لننتقل إلى خادوم التابع. الخطوة الثالثة: إعداد Redis التابع نحتاج للقيام ببعض التعديلات التي ستسمح لخادوم التابع بالاتّصال مع المتبوع: افتح الملف etc/redis/redis.conf/ بمحررك المُفضل: sudo nano /etc/redis/redis.conf عدّل الأسطر التالية، بعض الإعدادات ستكون تماما مثل الإعدادات الخاصة بالمتبوع. اجعل الخادوم متاحا للجميع على الويب بتحويل هذا السطر إلى تعليق (عبر إضافة # إلى بدايته): #bind 127.0.0.1 يحتاج التابع كذلك إلى كلمة مرور لنستطيع إعطاء الأوامر له (مثل INFO). أزل التعليق عن هذا السطر وضع كلمة سر للخادوم: requirepass your_redis_slave_password أزل التعليق عن هذا السطر وضع عنوان IP الخاص بالمتبوع، متبوعا برقم المنفذ المضبوط على الخادوم. افتراضيّا يكون الرقم 6379 هو رقم المنفذ: SLAVEOF your_redis_master_ip 6379 تأكد من تغيير your_redis_master_ip إلى عنوان IP الخاص بالمتبوع. احفظ الآن هذه التغييرات، واخرج من الملف. ومن ثم، أعد تشغيل الخدمة كما فعلنا مع خادوم المتبوع: sudo service redis-server restart هذا الأمر سيعيد ضبط Redis وسيحمل الملفات التي عدّلناها. اتصل بـredis: redis-cli -h 127.0.0.1 -p 6379 استوثق بكلمة المرور الخاصة بالتابع: AUTH your_redis_slave_password لقد شغّلنا الآن عنقود Redis يعمل كتابع ومتبوع، مع ضبط كل من الخادومين بشكل صحيح. الخطوة الرابعة: تحقق من نسخ التابع-المتبوع سيسمح لنا اختبار هذا التّنصيب فهم كيفية تصرف خوادم Redis بشكل أفضل، عندما نرغب في بداية برمجة الحماية من الفشل. ما سنفعله الآن هو التحقق من أن الإعدادات تعمل بشكل صحيح، وأن المتبوع يتواصل مع التابع بشكل صحيح. أولا، نتصل بـredis عبر الطرفيّة، على خادوم المتبوع: اتّصل أولا بالنموذج المحليّ، مع استعمال المنفذ الافتراضي 6379. إذا كنت قد غيّرت المنفذ، فغير الأمر بما يناسب: redis-cli -h 127.0.0.1 -p 6379 الآن، استوثق مع Redis باستعمال كلمة المرور التي وضعتها عند ضبط المتبوع: AUTH your_redis_master_password يجب عليك أن تحصل على OK كمُخرج. الآن، كل ما عليك فعله هو تنفيذ الأمر: INFO سترى كلّ ما تحتاج معرفته عن خادوم المتبوع. ما يهمنا هو القسم Replication# والذي يجب أن تبدو كالمخرجات التاليّة: . . . # Replication role:master connected_slaves:1 slave0:ip=111.111.111.222,port=6379,state=online,offset=407,lag=1 master_repl_offset:407 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:406 . . . لاحظ سطر connected_slaves:1، الذي يخبرنا بأن نموذج التابع يتواصل مع الخادوم المتبوع. كما يُمكنك ملاحظة أنّنا حصلنا على عنوان IP الخاص بالتابع، وكذلك المنفذ، الحالة، ومعلومات أخرى. الآن لنلق نظرة على قسم Replication# في الخادوم الخاص بالتابع، العمليّة نفسها التي قمنا بها مع المتبوع، ادخل إلى Redis، طبق الأمر INFO، وانظر إلى المُخرجات: . . . # Replication role:slave master_host:111.111.111.111 master_port:6379 master_link_status:up master_last_io_seconds_ago:3 master_sync_in_progress:0 slave_repl_offset:1401 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 . . . يُمكننا ملاحظة أن هذا الخادوم يقوم بدور التابع، وأنه يتواصل مع خادوم Redis المتبوع، ولا يملك أي توابع خاصة به. الخطوة الخامسة: التبديل إلى التابع بناء هذه الهندسة يعني أننا نريد التعامل مع الأخطاء والفشل بطريقة يُمكننا التأكد بها من سلامة البيانات وفي أقل وقت توقف ممكن لتطبيقنا. يُمكن لأي تابع أن يرتقي ليصبح متبوعا. أولا، لنختبر التبديل بشكل يدوي. على خادوم التابع، يجب أن نتصل مع نموذج Redis: redis-cli -h 127.0.0.1 -p 6379 الآن قم بالاستيثاق مع Redis باستخدام كلمة المرور التي استخدمتها عند ضبط التابع. AUTH your_redis_slave_password قم بإنهاء دور التابع: SLAVEOF NO ONE يجب الحصول على OK كمُخرج. الآن، نفذ الأمر: INFO قسم Replication# في الرد يجب أن يبدو كالمخرجات التاليّة: . . . # Replication role:master connected_slaves:0 master_repl_offset:1737 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 . . . كما هو متوقع، فقد تحول التابع إلى متبوع، وهو الآن جاهز لقبول اتّصالات من خوادم أخرى (إذا كانت موجودة). يُمكننا استعماله كمخزن نسخ احتيّاطي مؤقّت أثناء إصلاح الأخطاء في المتبوع الأصلي. إذا كنت تمتلك عدة توابع مُعتمدة على المتبوع الرئيسي، فسيتوجب عليك توجيههم إلى المتبوع الجديد. يُمكن القيّام بذلك بسهولة، بالخطوات التاليّة التي يجب تطبيقها في حال كُشِف عن أيّ أخطاء. من التطبيق، أرسل جميع الطلبات لـredis إلى خادوم تابع. على هذا التابع، نفّذ الأمر SLAVEOF NO ONE. منذ النسخة 1.0.0 من Redis هذا الأمر يُخبر التابع بالتوقف عن نسخ البيانات، والبداية في التصرف كخادوم متبوع. على جميع التابعين المتبقّين (إذا كانوا موجودين)، تشغيل الأمر SLAVEOF hostname port سيوجههم للتوقف عن النسخ من المتبوع القديم، وتجاهل البيانات، والبداية في النسخ من المتبوع الجديد. تأكد من تغيير hostname و port بالمعلومات المناسبة من المتبوع الجديد. بعد حل المشكلة، قد يرجع المتبوع القديم كمتبوع رئيسي، إذا كانت الإعدادات الخاصّة بك تتطلّب ذلك. هناك العديد من الطرق الممكنة لتنفيذ الخطوات أعلاه. ومع ذلك، الأمر يعود لك لتنفيذ أي حل تراه ملائما لبيئتك، وتأكد من اختبارها قبل أن يحدث أي فشل حقيقي. الخطوة السادسة: إعادة الاتصال بالمتبوع الأصلي لنرجع الاتصال بالمتبوع الأصلي. على الخادوم التابع ادخل إلى Redis وشغل الأمر التالي: SLAVEOF your_redis_master_ip 6379 تأكد من تغيير your_redis_master_ip إلى عنوان IP الخاص بالمتبوع إذا قمت بتشغيل الأمر INFO مجددا، يجب أن تلاحظ أنّنا عدنا إلى الإعدادات الأصليّة. في الختام لقد قمنا بإعداد بيئة مكونة من خادومين بنجاح، واحد يتصرف كمتبوع Redis والآخر ينسخ البيانات كتابع. بهذه الطريقة، إذا كان خادوم المتبوع يعاني من أي أخطاء أو فقد بياناتنا علي، فقد تعلمنا كيفية تبديل تابع ليصبح متبوعا كاحتياط إلى حين إصلاح المشكلة. الخطوات التالية تتعلق ببرمجة إجراء الحماية من الخطأ التلقائي، أو ضمان اتصالات آمنة بين جميع خوادمك باستعمال حلول VPN مثل OpenVPN أو Tinc، اختبار الإجراءات والسكربتات للتحقق من صحة إعداداتك. إضافة إلى ذلك، يجب عليك اتخاذ احتياطات عند نشر مثل هذه لإعدادات على بيئات الإنتاج. يجب أن تدرس توثيق Redis ويجب أن تفهم جيدا أي من نماذج الأمان هي الملائمة لتطبيقك. نستعمل عادة Redis كمَخزن للجلسة، وما يحتويه من معلومات قد يكون ذا قيمة لمهاجم ما. الممارسة الشائعة هي أن تمتلك هذه الخواديم إمكانية وصول فقط عبر شبكة خاصة private network. هذه نقطة بداية بسيطة عن كيفية بناء مخزن بيانات خاص بك؛ وليس درسا شاملا عن ضبط Redis لاستخدام هندسة تابع-متبوع. إذا كان هناك أي شيء كنت ترغب في تغطيّته في هذا الدرس، اترك تعليقات أسفله، ولمزيد من المعلومات عن هذا الموضوع، فإن قسم أسئلة DevOps بالأكاديمية تعتبر أماكن جيّدة لتبدأ منها. ترجمة -وبتصرّف- للمقال How To Configure a Redis Cluster on Ubuntu 14.04 لصاحبه Florin Dobre.
  20. الطّريقة الأنجح لمنع مُستخدم من الوصول إلى الموقع هي الحظر عن طريق الـIP. ثمّ إنّ الحظر لا يعتبر دائما الحل الأمثل، إذ يجب عليك أن تتأكّد من حماية الموقع جيّدا، وذلك عن طريق الالمام بمُختلف الثّغرات الشائعة مثل XSS و Man-in-the-middle. وفرض قيود مُعيّنة على المُستخدمين كالتّسجيل قبل انشاء المُحتوى وما إلى ذلك.
  21. الخطأ يُفيد بأنّ التّطبيق لم يتمكن من الاتّصال بقاعدة بيانات. عندما تطوّر تطبيقا محليّا، فقد ربطته بقاعدة بيانات، وعند نشر الموقع على الاستضافة، فإنّ رفع الملفات البرمجيّة أمر لا يكفي بل يجب عليك أن تقوم بإعداد قاعدة البيانات كذلك. لذلك تأكّد من انشاء قاعدة البيانات في الإستضافة (ستجد الخيار في لوحة التّحكم). إذا كنت قد أنشأت قاعدة البيانات مُسبقا فاتّصل بالدّعم الفني وأخبرهم عن المُشكلة.
  22. هل لك أن تُوفّر المعلومات التالية: لغة البرمجة المُستخدمة (php، ruby، python...) إطار العمل (Laravel، Djando، Rails...)
  23. مرحبا بك يا صديقي. من واقع تجربتي مع تطوير تطبيقات الويب، أستطيع أن أفهم وضعيّتك بشكل واضح، إذ مررتُ من قبل بما تمرّ به الآن، ونصيحتي لك هي أن تتعلّم بالتّطبيق وأن تتوقّف عن مُشاهدة وقراءة الدّروس دون تطبيق، فحضور دورة لبناء تطبيق مُتكامل من الصّفر أمر لا يكفي لكي تُصبح مُطوّرا، حتى ولو كنت تفهم شيفرات HTML و CSS عندما تفحص عنصر المواقع، إذا لم تفتح المُحرّر المُفضّل لديك وتبدأ بكتابة الشيفرة مع المُدرّب فتأكّد بأنّك لن تستفيد شيئا. قبل قراءة ما يلي، سأنصحك بأن تبدأ بشيء صغير بلغة HTML ونسّقه بلغة CSS وبعدها أضف تعديلات شيئا فشيئا. الآن، إذا أردت فعلا بناء تطبيقات الويب من الصّفر فعليك اتّباع العديد من الخطوات، بحيث ينمو تطبيقك خطوة خطوة، لذا لا تتوقّع أن تبني موقع تويتر في أوّل شهر من التّعلم، عليك أن تكون صبورا عند تنفيذ كل خطوة ولا تنتقل إلى الخُطوة المواليّة إلا بعد أن تتأكّد من أنّك أنهيت الخطوة التي تعمل عليها، وإليك الخطوات التي يجب عليك اتّباعها مع التّقنيات واللغات التي ستُساعدك لإنهاء الخطوة والانتقال إلى الأخرى: تصميم الشّكل البدئي للموقع: هنا لن تحتاج سوى لورقة وقلم، ارسم شكل الموقع الذي ترغب في بنائه لكي تمتلك فكرة أفضل، يُمكنك كذلك أن تستعين ببرنامج للتّصميم ولكنّ الأمر يبقى اختياريا. تصميم قاعدة البيانات: هنا ستحتاج كذلك إلى ورقة وقلم فقط، اكتب وارسم خطاطات لكيفيّة تصرّف البيانات في التّطبيق. مثلا لنقل بأنّك تبني مُدوّنة بسيطة، ستكون مُحتويات الورقة شيئا كالتالي: المُستخدم يُدخل اسمه وكلمة مروره--> يعني بأنّ لدينا جدولا للمُستخدمين يحتوي على عمودين "الاسم" و"كلمة المرور". بعدها ينشئ المُستخدم تدوينة جديدة مع عنوان مُناسب--> ما يعني بأنّنا نمتلك جدولا للتدوينات، مع عمودين "المُحتوى" و"العنوان". تهيئ قاعدة البيانات، هنا ستحتاج إما للغة SQL أو تعلّم كيفيّة استخدام ORM خاص باللغة التي تبني بها تطبيقك للتعامل مع قاعدة البيانات بسهولة، المهم بأنّك ستحتاج إلى تعلمّ مبادئ قواعد البيانات، ولا تقلق فالأمر بسيط جدا ويتطلب القليل من الصبر والاجتهاد. المرحلة الأخيرة هي العمل الفعلي على التّطوير، هنا ستحتاج إلى تعلّم لغة Php أو ما شابهها. وستحتاج إلى HTML و CSS لتنسيق الموقع. عليك أن تبدأ بشيء صغير جدا، لا تبدأ في التّفكير ببناء تطبيق بعشرات الميّزات، إذا بنيت موقعا يُمكّن المُستخدمين من نشر مقالاتهم فقط فهذا جيد، لا تشغل بالك بإضافة ميّزة التّعليقات والتّقييم وغيرها… فكلّها خطوات مُتقدّمة يجب عليك ألا تبدأ بها إلا بعد أن تنتهي من الخطوات الأولى. تذكّر كذلك بأنّك لا تعرف كلّ شيء وعاجلا أم آجلا ستجد نفسك تقع في الأخطاء. لا تخجل من السؤال، وابحث عن الطّريقة الصحيحة للقيام بالأمر بالاستعانة بمُحرّك بحث. تذكّر بأنّ البداية صعبة، ولكنّ الأسوأ هو أن لا تبدأ أبدا
  24. بعد أن تعلّمنا في الدّرس السّابق كيفيّة التّعامل مع التّعابير الشّرطية، وكيفيّة استعمال الجمل الشّرطية في برامجنا وكيفّية القيّام بإزاحة مناسبة عند التّعامل مع أجزاء متعدّدة من الشّيفرة في لغة بايثون، سنكمل مشوار تعلّم هذه اللغة الجميلة. سنتعلّم في هذا الدّرس كيفيّة التّعامل مع حلقات التّكرار مثل حلقة for وحلقة while. مع التذكير بأنّ جميع الشّيفرات التّي تبدأ بعلامة <<< يجب أن تنفّذ على مفسر بايثون. تُمكّنك الحلقات من تكرار شيفرة عددا من المرّات، يُمكنك أن تحدّد عدد مرّات التّكرار حسب إرادتك. حلقة While حلقة While تقوم بتكرار شيفرة ما عندما يكون الشّرط محقّقا ولا تتوقّف إلا عندما يكون الشّرط خاطئا، ولإنشاء حلقة while يجب تحديد عدد المرّات التّي تتكرّر فيها ووضعها كشرط ثم زيادة قيمة مُتغيّر بواحد، بحيث يزداد إلى أن يصل إلى العدد المُحدّد في الشّرط فيتوقّف. بإمكانك أيضًا تحديد شرط من نوع آخر بحيث لا ترتبط الحلقة بتنفيذ الشيفرة بعدد مُعيّن من المرّات وإنما بتحقق شرط مُعيّن (أن تصبح قيمة مُتغيّر مُخالفة لقيمة نقوم بتحديدها) لنقل بأنّنا نريد طباعة "Hello" مائة مرّة، بالطّبع قد تقول أكرّر الجملة التالية مائة مرة: print "Hello" هذه الطّريقة صحيحة لكنّها تُكلّف الكثير من الوقت، والمُبرمج دائما ما يُفكّر بطريقة أذكى (افعل أكثر عدد ممكن من المهام في أقل وقت ممكن). لذلك لا يُمكننا اعتماد هذه الطّريقة. وتُقدم لنا لغات البرمجة خاصية التكرار ببساطة وبأقل عدد من الأسطر. كمثال يُمكننا طباعة "Hello" عشر مرّات بالطّريقة التّاليّة، لاحظ بأنّ الجمل التّابعة للكلمة while مُزاحةٌ بأربع مسافات بيضاء: >>> i = 0 >>> while i < 10: ... print "Hello" ... i = i +1 Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello شرح المثال أعلاه: السّطر الأول: نقوم بوضع قيمة بدئية للمتغيّر i. السّطر الثّاني: نقوم بإضافة الشّرط وهو أن تكون قيمة المتغير أصغر من العدد عشرة فإن كان يُساوي 10 يتوقّف البرنامج. السّطر الثّالث: نقوم بطباعة الكلمة السّطر الرّابع: نقوم بزيّادة قيمة المتغير في المرّة الأولى سيقرأ المفسّر قيمة المُتغيّر فيجدها تُساوي العدد 0، ثمّ يتحقّق من أنّ شرط أن تكون القيمة أصغر من 10 صحيح، وبما أنّ الشّرط مُحقّق في المرّة الأولى (بالطّبع لأنّ 0 أصغر من 10) سيُتابع البرنامج العمل، وسيطبع جملة التّرحيب "Hello" بعد ذلك يزيد المُفسّر قيمة المتغيّر i بواحد (لتكون القيمة الجديدة هي 1) يتحقّق المُفسّر من صحّة الشّرط مُجدّدا وبما أنّه مُحقّق فقد طُبعت الجملة "Hello" مرّة ثانيّة، بعدها يزيد من قيمة المُتغيّر بواحد مجدّدا لتكون القيمة الجديدة للمُتغيّر i هي العدد 2 والذي بدوره أصغر من 10 فتُطبع الجملة مرّة ثالثة. وهكذا دواليك إلى أن تصل قيمة المُتغيّر إلى العدد 10 فيُصبح الشّرط خاطئا (لأنّ العدد 10 ليس أصغر من 10 بل مُساو له)، وبالتّالي يتوقّف التّكرار. لتستوضح الأمر بشكل أفضل، يُمكنك أن تطبع قيمة المُتغيّر i عند كلّ تكرار، انظر المثال: >>> i = 0 >>> while i < 10: ... print "Hello", i ... i = i +1 ... Hello 0 Hello 1 Hello 2 Hello 3 Hello 4 Hello 5 Hello 6 Hello 7 Hello 8 Hello 9 أمثلة تطبيقية لاستعمال حلقة While مثال 1: الوصول إلى عناصر قائمة، مجموعة أو صف هناك عدّة أمثلة تطبيقيّة لاستعمال حلقة While، وعلى سبيل المثال يُمكن أن تُستخدم حلقة التّكرار هذه للدّوران على قائمة وطباعة عناصرها، انظر المثال التّالي: >>> I = 0 >>> children = ['Omar','Khaled','Hassan','Zaid','Youssef'] >>> while i < len(children): ... print children[i] ... i = i + 1 ... Omar Khaled Hassan Zaid Youssef في المثال أعلاه، قُمنا بإنشاء قائمة باسم children تحتوي على خمسة عناصر، ثمّ قُمنا بطباعة كلّ عنصر عند كلّ تكرار، وقد وضعنا وجوب أن تكون قيمة المُتغيّر i أصغر من عدد عناصر القائمة والتّي حدّدناها بمُساعدة الدّالة len، وذلك ليتوقّف البرنامج عند وصول قيمة المتغير إلى العدد 5. مُلاحظة: هذا مجرّد مثال لكيفيّة استخدام حلقة While. وعادة ما تُستخدم الحلقة for لمثل هذه التّطبيقات (الدّوران على القوائم، المجموعات والصفوف…) وذلك لأنّها أكثر مرونة، وفي النّهاية يعود الاختيار لك (ويُمكنك استخدام الطّريقة التّي تُناسبك). دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن مثال 2: عمل البرنامج إلى أن يجيب المستخدم بجواب معين لنقل بأنّك سألت المُستخدم سؤالا رياضيا (مثلا: "ما جمع 4 زائد 3" )، وتريد تهنأته عند إدخال الجواب الصحيح، الأمر بسيط، ولا يتعدى بضعة أسطر، افتح ملفّا وسمّه Answer.py، وضع فيه الأسطر التّالية: number = raw_input('Enter the Result of 3+4: ') if int(number) == 7: print 'Congratulation' نستخدم الدّالة int هنا لتحويل القيمة المُدخلة من طرف المُستخدم إلى قيمة من نوع integer أي عدد صحيح، وهذا مُفيد لأنّ القيمة التّي تُستقبل في الدّالة raw_input تكون عبارة عن سلسلة نصيّة. بعد أن قُمنا بجزء من المطلوب لنفترض بأنّ المُستخدم قد أدخل إجابة خاطئة، الأمر الذي قد يخطر في بالك الآن هو طباعة جملة تُفيد المُستخدم بأنّ إجابته خاطئة والخروج من البرنامج، الأمر جيّد ، ولكن ماذا لو أردنا أن نوفّر للمُستخدم حقّ المُحاولة من جديد، بحيث يطبع البرنامج جملة تفيد بأنّ الإجابة المُدخلة خاطئة ثمّ يطلب من المُستخدم أن يُحاول مجدّدا مع توفير إمكانيّة إدخال قيمة جديدة، يُمكن أن نقوم بذلك بالاستعانة بجملة else انظر المثال التّالي: number = raw_input('Enter the Result of 3+4: ') if int(number) == 7: print 'Congratulation!' else: print 'Sorry, Your Answer is not correct, please try again:' number = raw_input('Enter the Result of 3+4: ') if int(number) == 7: print 'Congratulation!' هذه الشّيفرة جيّدة لكنّها غير عمليّة بتاتا، إذ تستخدم الكثير من التّكرار الذي لا فائدة منه، كما أنّها لا توفّر للمُستخدم سوى محاولة واحدة أخرى. فماذا لو أردنا أن نوفّر للمُستخدم عددا أكبر من المُحاولات؟ قد تقول ننسخ الشّيفرة أعلاه ونكرّرها عددا من المرّات في برنامجنا، هذا الحلّ سيء جدّا وذلك لأنّه لا يلبّي رغبتنا في توفير عدد لا نهائي من المحاولات للمُستخدم (بحيث لا يتوقّف البرنامج إلا عندما يجد المُستخدم الإجابة الصّحيحة)، كما أنّ نسخ الشّيفرة وتكرارها عدّة مرّات سيجعل من البرنامج طويلا وصعب القراءة. لحلّ هذه المُسألة، يُمكننا ببساطة أن نكرّر الشّيفرة التّي تتحقّق من الإجابة كلّ مرّة تُخالف فيه الإجابة التّي أدخلها المُستخدم، يعني يُمكننا أن نُخبر البرنامج بالتّحقّق من الإجابة إذا كانت صحيحة فكل شيء جيّد ونطبع رسالة تهنئة، إذا كانت الإجابة خاطئة، فسنعيد الشّيفرة مُجدّدا، ونعيد السّؤال إلى أن يصل المُستخدم إلى الإجابة الصحيحة، والتّالي مثال على الشيفرة التي تؤدي الغرض: number = raw_input('Enter the Result of 3+4: ') while int(number) != 7: number = raw_input('Wrong answer please try again, enter the Result of 3+4: ') print 'Congratulation' السّطر الأول: يقوم البرنامج بطلب الإجابة من المُستخدم. السّطر الثاني: تتحقّق حلقة While من كون الإجابة خاطئة، إذا تحقّق الشّرط، ينتقل البرنامج إلى السّطر الثالث، إذا لم يتحقّق (أي قيمة المتغير تساوي 7)، ينتقل البرنامج إلى السّطر الرابع. السّطر الثالث: يقوم البرنامج بإخبار المُستخدم بأنّ الإجابة خاطئة ويطلب منه إعادة المُحاولة. السّطر الرابع: يطبع البرنامج جملة التهنئة. حلقة التكرار For حلقة For جملة برمجية أخرى تُمكّن من التكرار، وهي من الأساليب المُستعملة للدوران حول قيم القوائم كذلك، وتتميّز بتعريف مُتغيّر العدّ في بداية الحلقة، أي أنّك على خلاف حلقة While لا تحتاج إلى تعريف المُتغيّر قبل البدء بكتابة الحلقة، كما أنّ حلقة for لا تحتاج إلى شرط معيّن بل تُحدّد عدد مرّات التّكرار حسب عدد عناصر القائمة المُسندة. انظر المثال التّالي، لاحظ بأنّ الجمل التّابعة للسّطر (for counter in range(1, 11 مُزاحةٌ بأربع مسافات بيضاء (انظر درس الإزاحات والمساحات البيضاء). >>> for counter in range(1, 11): ... print counter, 'Hello World!' ... 1 Hello World! 2 Hello World! 3 Hello World! 4 Hello World! 5 Hello World! 6 Hello World! 7 Hello World! 8 Hello World! 9 Hello World! 10 Hello World! في المثال أعلاه أنشأنا حلقة تكرار تدور على قائمة أنشأناها باستخدام الدّالة range، وأسندنا قيم التّكرار إلى المتغيّر counter الاختلاف هنا عن حلقة while هو أنّ المُتغيّر counter يعتبر جزءا من قائمة وليس مُتغيّرا يحمل قيمة في كلّ تكرار. مُلاحظة: لدّالة range تقوم بإنشاء قائمة أعداد مرتّبة من الأصغر إلى الأكبر حسب عاملين الأول رقم البداية والثّاني يُمثّل النّهاية مع ملاحظة بأنّ الرّقم النّهائي في السّلسلة لا يحتسب (في المثال التّالي 1 هو العدد الأول والعدد النّهائي هو العدد 11 ). >>> range(1,11) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] يُمكن إنشاء قائمة معكوسة بتمرير معامل ثالث بقيمة 1- وقلب المعاملات: >>> range(10, 0, -1) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] تُمكّنُ حلقة for من الدّوران على عناصر مجموعة ما (القوائم الصفوف، السّلاسل النّصيّة…) عن طريق تمرير اسم المُتغيّر بعد كلمة in في الشيفرة، لاحظ بأنّ الجمل التّابعة للسّطر for lettre in v مُزاحةٌ بأربع مسافات بيضاء (انظر الدّرس السّابق). >>> v = "Hello" >>> for lettre in v: ... print lettre ... H e l l o في المثال أعلاه، قمنا بتعيين القيمة "Hello" للمتغير v وبعدها استخدمنا حلقة for لكي نطبع كل حرف من هذه الكلمة والذي عيّناه للمتغير lettre. كما رأينا من قبل يُمكن كذلك استخدام for لتكرار شيفرة ما عددا من المرّات، كما فعلنا مع حلقة while بمُساعدة الدّالة range: for i in range(0,100): print i يُمكن كذلك أن نستعمل حلقة for لنقوم بالمرور على مفاتيح قاموس ما لنحصل على كل مفتاح على حدة، انظر المثال التالي: >>> a = {'x': 1, 'y': 2, 'z': 3} >>> for key in a: ... print key ... x y z وكما قلنا سابقا في الدّرس الرابع، يُمكنك الحصول على كل من مفاتيح وقيم وعناصر قاموس على شكل قائمة: >>> a = {'x': 1, 'y': 2, 'z': 3} >>> a.keys() ['x', 'y', 'z'] >>> a.values() [1, 2, 3] >>> a.items() [('x', 1), ('y', 2), ('z', 3)] ومما سبق نستنتج بأنّنا نستطيع الحصول على كل واحدة من قيم القاموس: >>> a = {'x': 1, 'y': 2, 'z': 3} >>> for value in a.values(): ... print value ... 1 2 3 وبالتّالي يُمكنك أن تستنتج بأنّنا نستطيع المرور على كل من مفاتيح القواميس وقيمها معا في نفس الوقت: >>> a = {'x': 1, 'y': 2, 'z': 3} >>> for key, value in a.items(): ... print key, value ... x 1 y 2 z 3 أمثلة تطبيقية لاستعمال حلقة For مثال 1: طباعة مفاتيح وقيم قاموس معين يُمكننا أن نستعمل حلقة for لطباعة كل مفتاح وقيمته في قاموس ما. لنقل مثلا بأنّنا نمتلك قاموسا يحتوي على أسماء الألوان وشيفرة كلّ لون بنظام hex (نظام يستخدمه مطورو الويب لإسناد لون مُعيّن لعنصر ما، فعوضا عن كتابة اسم اللون تُستخدم شيفرته)، ونُريد أن نطبع كل لون متبوعا بشيفرته، القاموس الذي نمتلكه هو كالتّالي: colors = { "red":"#f00", "green":"#0f0", "blue":"#00f", "cyan":"#0ff", "magenta":"#f0f", "yellow":"#ff0", "black":"#000" } ونريد أن نعرض هذه المفاتيح مع قيمها بشكل لطيف، هل يُمكنك أن تُفكّر في طريقة لإنجاز مرادنا؟ الحل يكون باستخدام حلقة for للمرور على كل مفتاح وقيمته ومن ثمَّ طباعتها، يُمكننا القيام بذلك بسطرين فقط من شيفرة بايثون، وذلك كالتّالي (ضعها في ملف بامتداد py ونفّذ الملف). colors = { "red":"#f00", "green":"#0f0", "blue":"#00f", "cyan":"#0ff", "magenta":"#f0f", "yellow":"#ff0", "black":"#000" } for color_name, hex_value in colors.items(): print color_name + ': ' + hex_value مُخرجات البرنامج ستكون كالتّالي: blue: #00f yellow: #ff0 green: #0f0 cyan: #0ff magenta: #f0f red: #f00 black: #000 هذا مُجرّد مثال بسيط على استخدام كل من حلقة for والقواميس على أرض الواقع، ولا يجب عليك أن تضع حدّا لإبداعك، إذ يُمكنك استخدام ما تعلّمته بطرق مُختلفة وبرمجة برمجيات تقوم بوظائف لا محدودة، لذا لا تربط علمك بهذه الأمثلة، بل فكّر في طريقة للاستفادة مما تعلّمته لإنتاج برامج أكثر تعقيدا وذات وظائف مُتعدّدة، وسيُسعدني إن شاركت برامجك معنا. إيقاف حلقة بجملة :break يُمكن أن توقف حلقة تكراريّة من نوع while عند نقطة معيّنة بالجملة break ويُمكن تحديد نقطة التوقف باستخدام جملة if الشّرطيّة، انظر المثال: >>> i = 0 >>> while i < 30: ... if i > 15: ... print "Stop the loop" ... break ... print i ... i = i + 1 ... 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Stop the loop في المثال أعلاه، كان من المُفترض أن تُكمل الحلقة التنفيذ إلى أن يصل المُتغيّر iإلى القيمة 30، ولكنّه توقّف بمُجرّد أن وصلت قيمة المُتغيّر إلى 15 وهذا لأنّنا وضعنا شرطا لإيقاف التكرار عندما تُصبح قيمة i أكبر من 15 وبما أنّ الشّرط أصبح صحيحا فقد توقّف البرنامج لأنّنا أمرناه بالجملة break. ويُمكن كذلك أن توقف حلقة تكراريّة من نوع for عند نقطة معيّنة بالجملة break ويُمكن تحديد نقطة التوقف باستخدام جملة if الشّرطيّة: >>> list = [1,5,10,15,20,25] >>> for i in list: ... if i > 15: ... print "Stop the loop" ... break ... print i ... 1 5 10 15 Stop the loop في المثال أعلاه، توقّف الدوران على القائمة list بعد أن وصل المتغيّر إلى القيمة 15، وهذا هو الشرط الذي وضعناه في جملة if. تجاهل تنفيذ الشيفرة في حلقة بجملة :continue يُمكن أن توقف حلقة تكراريّة من نوع for عند نقطة معيّنة ثمّ تُكمل التكرار في الخطوة التّاليّة، أي قفز خطوة عند التّكرار، وذلك بالاستعانة بالجملة continue ويُمكن تحديد نقطة التوقف باستخدام جملة if الشّرطيّة، يعني أنّك تستطيع إخبار البرنامج بالانتقال إلى التنفيذ التّالي إذا ما تحقّق هذا الشّرط، انظر المثال: >>> list = range(1, 31) >>> for i in list: ... if i == 15: ... print "Continue the loop" ... continue ... print i ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Continue the loop 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 في المثال أعلاه، كان من المُفترض أن تُكمل الحلقة التنفيذ إلى أن يصل المُتغيّر iإلى القيمة 30، ولكنّه توقّف بمُجرّد أن وصلت قيمة المُتغيّر إلى 15 وتابعت عندما وصلت القيمة إلى العدد 16 وهذا لأنّنا وضعنا شرطا لإيقاف التكرار عندما تُساوي قيمة i العدد 15 وبما أنّ الشّرط قد تحقّق فقد توقّف البرنامج عند تلك النّقطة وطبع جملة Continue the loop لأنّنا أمرناه بالجملة continue. تمارين صديقي القارئ، يجب أن تعرف بأنّ التّعلم لا يكتمل إلا بتطبيق المُكتسبات، لذلك فالتّفكير في حلول هذه التّمارين أمر فعال جدا ومُفيد لك، لذلك لا تكن كسولا، ولا تكتف بالقراءة فقط، بل عليك تطبيق ما تعلّمته وإنشاء برامجك الخاصّة، خاصّة وأنّك إلى الآن قد تعرّفت على جزء كبير من تقنيات البرمجة التي تسمح لك بإنشاء برامج مُبهرة، تُؤدي أغراضا عديدة. يُمكنك أن تنشر حلول التّمارين في صندوق التعليقات إذا كنت ترغب في ذلك، ومن المفضل أن تُحاول حل التّمارين بنفسك قبل أن تنظر إلى أجوبة زملائك. تمرين 1 اكتب برنامجين يقومان بطباعة جملة: Hello World! 1000 مرة. البرنامج الأول باستخدام حلقة While والثاني بحلقة For. تمرين 2 اكتب برنامجا يقوم بأخذ قيمة من المُستخدم ثمّ يطبعها 10 مرات. تمرين 3 ما مُخرجات البرنامج التّالي: number = 3 list = range(1, 31) for i in list: if i == number: continue if 15 < i < 21: continue print i تمرين 4 اكتب برنامجا يقوم بطباعة عناصر القائمة التّالية مع جملة ترحيب. list = ['Ahmed', 'Abdelhadi', 'Dyouri', 'Hossam', 'Mohammed', 'Khaled', 'Ibrahim', 'Othman'] يجب أن تكون مُخرجات البرنامج كالتّالي: Hello, Ahmed Hello, Abdelhadi Hello, Dyouri Hello, Hossam Hello, Mohammed Hello, Khaled Hello, Ibrahim Hello, Othman تمرين 5 عد إلى الدّرس السّابق وعدّل برنامج تسجيل الدخول لكي يقبل مُحاولة المُستخدم من جديد إذا كانت كلمة السّر خاطئة. ولا تنس أن تنشر برنامجك في قسم التّعليقات على هذا المقال إذا أردت ذلك. ترجمة -وبتصرف- من الكتاب Python Practice Book لصاحبه Anand Chitipothu.
  25. أنت كاتب مُستقل أليس كذلك؟ إذا كنت كذلك، فما دليلك؟ إذا لم تكن تملك دليلا على أنك مُدون فعليك ببناء واحد الآن، وعليك أن تعلم بأنّ أهم مُعين على التّسويق هو عملك السابق، لذلك إليك بعضا من الأشياء التي يجب عليك فعلها قبل أن تُحاول الحصول على العملاء: اكتب في مُدونتك: يجب عليك أن تعرف بأنّ مُدونتك هي سيرتك الذاتية في عالم العمل الحر، قد تنجح في كسب العملاء دون مدونة ولكن الأمر صعب، لذلك افتح مدونة واكتب فيها تجاربك وانشر مقالات مجانا، فبالعطاء تكسب. اكتب كتيبا صغيرا واطرحه مجانا: هل لديك خبرة في مجال مُعين؟ لماذا لا تكتب كتيبا وتطرحه مجانا، بهذا ستُعرّف العملاء المُحتملين على قدراتك ومواهبك وربما يتحول قراؤك إلى عملاء.
×
×
  • أضف...