Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
AhmadAlsaadi
GitHub Repository: AhmadAlsaadi/Arabic-python-notebook
Path: blob/master/deprecated/Introduction.ipynb
675 views
Kernel: Python 3 (ipykernel)
from IPython.core.display import HTML def _set_css_style(css_file_path): """ Read the custom CSS file and load it into Jupyter. Pass the file path to the CSS file. """ styles = open(css_file_path, "r").read() s = '<style type="text/css">%s</style>' % styles return HTML(s) _set_css_style("custom.css")

بسم الله الرحمن الرحيم

المحتويات


مقدمة الكتاب
الفصل الاول: نبذة عن لغة بايثون
الفصل الثاني: المبادئ الاساسية للغة بايثون
الفصل الثالث: التعامل مع البيانات التجميعية
الفصل الرابع: اتخاذ القرارات
الفصل الخامس: حلقات التكرار
الفصل السادس: كتابة الدوال
الفصل السابع: التعامل مع الملفات
الفصل الثامن: التعامل مع المكتبات
الفصل التاسع: البرمجة الكائنية في بايثون

مقدمة الكتاب

الحمد لله الذي تتم بفضله الصالحات واصلي وأسلم على خير البشر نبينا محمد صلى الله عليه وسلم وعلى آله وصحبه الطيبين الطاهرين. اما بعد:
فلقد لاحظت خلال فترة تعلمي للغة بايثون افتقار المكتبة العربية الى كتاب مكتمل يشرح مبادي هذه اللغة بشكل منظم وبسيط. فعقدت العزم على تأليف هذه الكتاب وذلك لما رايته من الأهمية بمكان ان يتعلم القارئ العربي هذه اللغة والتي أصبحت اللغة البرمجية المحبوبة لدي الكثير من العلماء والباحثين والمهندسين. فمعظم جامعات العالم اليوم أصبحت تدرسها لطلابها لانها لغة برمجية سهلة و قوية في نفس الوقت ويمكن استخدامها في مجالات عدة. لذلك اردت ان يكون هذا الكتاب لبنة أولى للمساهمة في تعليم هذه اللغة وحافزا الى تدريسها في مدارسنا الحكومية في مراحل مبكرة كالمتوسطة والثانوية مثلا وذلك لان الأجيال الحالية لديها شغف غير مسبوق على التعرف على كل ماهو جديد في عالم التقنية وخصوصا الكمبيوترية منها. فكل ما يحتاجة هذا الجيل هو استخدام طريقة سهلة وشيقة تعرفهم بهذه اللغة وتعزز شغفهم بالتقنية فيصبحوا قادرين على تعلمها والاستفادة منها دون مشقة او عناء. وبما ان هذا العمل بشري المصدر فانه لا يصل الى درجة الكمال لذلك ارجو ممن سنحت له الفرصة لقراءة هذا الكتاب ان يساهم في تحسين محتواه بارسال ملاحظاته الى ايميل المؤلف [email protected] الذي يعدكم على اخذها في عين الاعتبار متى ما سنحت الفرصة لاصدار طبعة جديدة لهذا الكتاب.

الفصل الاول : نبذة عن لغة بايثون


تعريف لغة بايثون

بايثون (python) لغة برمجية مفتوحة المصدر (open source) من المستوى العالي (high level) سهلة التعلم يمكن الاعتماد عليها في كتابة الكثير من التطبيقات البرمجية القوية. وأكبر دليل على ذلك هو استخدام وكالة الارصاد الامريكية ناسا وشركتا قوقل وياهو وغيرها من الشركات الكبرى لهذه اللغة في بناء برامجهم المختلفة.

نشأة لغة بايثون

كانت بدايات نشأة هذه اللغة في هولندا على يد شخص يدعي جويدو فان روزم (Guido van Rossum) في نهاية الثمانيات الميلادية من القرن العشرين. حيث تم الاعلان عنها في عام ١٩٩١م. كما يعتبر فتح مصدر هذه اللغة من اهم الاسباب التي ادت الى زيادة شهرتها من خلال تكوين مجتمع برمجي نشط حولها اسهم في انشاء مكتبات كثيرة سهلت على المطورين الاخرين بناء تطبيقاتهم بسرعة و سهوله فائقة مقارنة باللغات البرمجية الأخرى.

مزايا لغة بايثون

للغة بايثون مزايا عدة جعلت منها اللغة المفضلة الاولى لدى كثير من المبرمجين ومن بين اهم هذه المزايا نذكر:
  1. سهولة التراكيب اللغوية: فاكوادها البرمجية تكتب بطريقة قريبة جدا من اللغة الانجليزية. لذلك نجدها لا تشكل اي عائق امام أي مبرمج ان يفهم الأكواد المكتوبة من قبل مبرمجين اخرين عندما يستدعي الامر صيانة تلك الاكواد او تحديثها.
  2. المرونة: يمكن تشغيل وتطوير البرامج المكتوبة بلغة بايثون على معظم انظمة التشغيل المعروفة. فالأكواد التي تم تطويرها على نظام ويندوز يمكن تشغيلها على نظام ماك ولينكس والعكس صحيح دون الحاجة الى اعادة بناء الأكواد (compiling).
  3. كثرة المكتبات: يعتبر توفر المكتبات من اهم المزايا التي تقدمها اللغة للمبرمجين لتزيد من فعاليتهم في بناء التطبيقات. لذلك عند تنصيب اصدارة بايثون نجد انها تحتوي على مكتبات قياسية كثيرة بعضها يعتبر جزء لا يتجزأ من تراكيب اللغة كمكتبة الارقام والقوائم وبعضها الاخر يعمل على تسهيل التعامل مع انظمة التشغيل اما الجزء الاكبر من هذه المكتبات فهو اختياري يتم استيراده متى ما دعت الحاجة لذلك. كما ان هناك مكتبات اخري تحتاج الى تنصيب قبل ان يتمكن المبرمج من استيرادها واستخدامها في برنامجه. وهذه المكتبات مجانية ويمكن تحميلها وتنصيبها اما من الموقع الخاص بالمطورين لهذه المكتبة او من موقع http://pypi.python.org والذي يحتوي حتى وقت كتابة هذا الكتاب على 69478 مكتبة مجانية جاهزة يمكن استخدامها في بناء التطبيقات المختلقة.
  4. التكامل مع لغات برمجية اخرى: يمكن استخدام بايثون كلغة مساندة تمكن المستخدم لبرنامج مكتوب بلغة سي (C) او سي بلس بلس (++C) مثلا من زيادة او تعديل خصائص ذلك البرنامج ليتناسب مع احتياج المستخدم. ومن أقرب الامثلة على ذلك هو استخدام لغة بايثون في برنامج فري كاد (FreeCAD) كلغة برمجة نصية للتحكم بكافة خصائص البرنامج ووظائفه.

اصدارات لغة بايثون

هناك اصدارتان لبايثون. الإصدارة الأولى تعرف ببايثون 2 وهي الاقدم والاصدارة الاخرى تدعى بايثون 3 وهي الاحدث.

توزيعات بايثون

بالاضافة الى التوزيعة الرسمية التي يمكن تحميلها من www.python.org هناك توزيعات اخري تأتي محملة بمكتبات ومدير ادارة مكتبات تهدف الى اراحة المستخدم من عناء تحميل المكتبات واضافتها للاصدارة الرسمية. معظم هذه التوزيعات تأتي على شكل اصدارة مجانية واصدارة تجارية.ومن بين هذه التوزيعات مايلي:
  • توزيعة Anaconda: يمكن تحميل هذه التوزيعة من www.continuum.io ويمكن تنصيب هذه التوزيعة على ويندوز وماك ولينكس. تحتوي التوزيعة على اكثر من 100 مكتبة و مدير ادارة مكتبات يدعى conda. كما يوجد لهذه التوزيعة اصدارة مصغرة تدعى miniconda واصدارات تجارية اخرى.
  • توزيعة Enthought Canopy: يمكن تحميل هذه التوزيعة من www.enthought.com/products/canopy . تأتي هذه التوزيعة باصدارة تجارية واصدارة مجانية لمدة سنه قابلة للتجديد للاكاديمين والطلاب و لكن بعد اجراء عملية التسجيل. تحتوي الاصدارة على مفسر بايثون 2 و اكثر من 450 مكتبة متخصصة للاغراض العلمية والتحليلية. كما ان عملية التسجيل تسمح للمستخدم بالاطلاع على الفيديوهات التعليمية المعمولة من قبل Enthought .
  • توزيعة ActivePython:

تنصيب بايثون

بما ان ميكروسوفت ويندوز مازال اكثر انطمة التشغيل شيوعا وخاصة في الوطن العربي فاننا سنبدأ بشرح طريقة تنصيب مفسر لغة بايثون على هذا النظام اولا.

تنصيب بايثون على نظام ويندوز:

كما ذكرنا سابقا فان بايثون ياتي بتوزيعة رسمية من موقع www.python.org وتوزيعات اخرى تحتوي على بعض الاضافات مثل Anaconda و Canopy وغيرها من التوزيعات وفي هذا المقام فإننا سوف نقوم بشرح طريقة تنصيب مفسر بايثون 3 من الموقع الرسمي وكذلك توزيعة anaconda لشهرتها الواسعة بين العلماء والباحثين. واليك الخطوات التالية التي تساعدك على عمل ذلك

أولاً: تصيب اصدارة بايثون الرئيسية

from IPython.lib.display import YouTubeVideo YouTubeVideo('Id_DbmP6PbI')

ثانياً: تصيب توزيعة Anaconda

from IPython.lib.display import YouTubeVideo YouTubeVideo('lo77KeWG11g')

تنصيب بايثون على نظام ماك ولينكس:

يأتي نظاما تشغيل ماك ولينكس وقد نصب عليهما مفسر بايثون ذو الاصدرة رقم 2. لكن هذه الاصدارة اصحبت قديمة ولا نريد ان نستخدمها في تعلمنا للغة بايثون هنا لذلك سوف نقوم بشرح طريقة تنصيب اصدارة بايثون 3 الأساسية و توزيعة Aanconda على نظام الماك ولينكس كما يلي:

اولاً: تصيب اصدارة بايثون الرئيسية على نظام ماك

from IPython.lib.display import YouTubeVideo YouTubeVideo('0uEW-xEX3V0')

ثانياً: تصيب توزيعة Anaconda على نظام ماك

from IPython.lib.display import YouTubeVideo YouTubeVideo('DbmP6PbI')

ثالثاً: تصيب اصدارة بايثون الرئيسية على نظام لينكس

from IPython.lib.display import YouTubeVideo YouTubeVideo('w9F0bmxCD-0')

رابعاً: تصيب توزيعة Anaconda على نظام لينكس

from IPython.lib.display import YouTubeVideo YouTubeVideo('0uEW-xEX3V0')

الفصل الثاني : المبادئ الأساسية للغة بايثون


بعد ان تأكدنا من ان نظام التشغيل الذي نعمل عليه يحتوي على احدى اصدارات لغة بايثون 3 يمكننا الان ان نبدأ رحلة التعلم والتى اتمنى ان تكون حافلة بالمتعة والفائدة.

أهداف الفصل

عند اتمام هذا الفصل يجب ان يكون لديك المام بعدة مبادئ اساسية عن لغة بايثون والتي من اهمها:
  1. طريقة كتابة المتغيرات
  2. طريقة تدوين الملاحظات على الكود البرمجي
  3. كيفية طباعة نص على شاشة الكمبيوتر
  • الفرق بين استخدام الاحرف الصغيرة والكبيرة في كتابة الكود البرمجي.
  • اجراء العمليات الحسابية الاساسية
  • انواع البيانات الاساسية في لغة بايثون.
  • القاعدة التي تحكم عدد المسافات الفارغة المتروكة قبل بداية كل سطر برمجي
  • المتغيرات

    المتغيرات هي اسماء تستخدم للدلالة على قيم بيانات موجودة في ذاكرة الكمبيوتر. واستخدام المتغيرات في كتابة الاكواد البرمجية ذو اهمية قصوى بحيث لا يكاد يخلو برنامج من وجود متغير واحد او اكثر وذلك لانها تسهل على المبرمج تذكر البيانات باسماء يسهل حفظها بدلا من استخدام قيم البيانات ذاتها. لاسناد قيمة الى متغير فاننا نختار اسما مناسب للمتغير ومن ثم نضع علامة اليساوي واخيرا نضع القيمة المراد اسنادها للمتغير كما في المثال التالي:
    x=5
    في المثال السابق علامة اليساوي تمسى معامل الاسناد. لذلك يصبح الان بمقدورنا استخدام x عوضا عن القيمة 5 في اي عمليات حسابية. كما يجب التنويه الى ان قيمة x قابلة للتغيير. فعند تكرار المثال السابق باستخدام القيمة 10 فان x اﻵن تصبح تشير للقيمة 10 عوضا عن القيمة 5 كما في المثال التالي:
    x2=5 y=10 2x+y
    File "/tmp/ipykernel_1153/2671339542.py", line 1 2x=5 ^ SyntaxError: invalid syntax

    القواعد التي تحكم كتابة اسماء المتغيرات

    ان لغة بايثون تتطلب التقيد بقواعد اساسية عند كتابة أسماء المتغيرات وتتلخص هذه القواعد فيما يلي:
    اولاً: لا يمكن استخدام اي رمز في كتابة المتغيرات عدا الاحرف والارقام والشرطة السفلية. كما في الامثلة التالية
    y_2=5
    y$="Saeed"
    File "/tmp/ipykernel_1153/3462219700.py", line 1 y$="Saeed" ^ SyntaxError: invalid syntax
    y=5-3 y
    2
    y&="Waleed"
    لاحظ انه عند استخدامنا للرمزين $ و & كجزء من اسم المتغير فان مفسر بايثون اعطانا رسالة بوجود خطأ في التركيب اللغوي "Invalid syntax"
    ثانياً: المتغيرات يجب ان تبدأ بحرف او شرطة سفلية. عدا ذلك فان مفسر بايثون يعطي رسالة بوجود خطأ. وهذه بعض الامثلة على هذه القاعدة:
    number=20
    _number=30
    2y=5
    لاحظ ان في المثال الاخير حاولنا كتابة متغير يبدأ اسمه برقم 2 لكن مفسر بايثون رفض ذلك واعطانا رسالة تفيد بوجد خطأ في التركيب اللغوي "invalid syntax"
    ثالثاً: يمكن استخدام الارقام في كتابة أسماء المتغيرات ولكن لايمكن استخدامها في بداية الاسم كما في الامثلة التالية:
    number2=7
    one2one=1
    رابعاً: أسماء المتغيرات يمكن ان تكون حرف كما وضحنا سابقا بالمتغير x او كلمة كما وضحنا ذلك بالمتغير name او مجموعة كلمات مربوطة بشرطة سفلية كما في المثال التالي:
    student_number=1234
    tag number= 444
    لاحظ انه عندما حاولنا كتابة اسم المتغير من كلمتين بترك فراغ يين الكلمتين رفض مفسر بايثون هذه العملية واعطانا رسالة مشابه للرسالة السابقة تفيد بوجود خطأ في التركيب اللغوي
    خامساً: أسماء المتغيرات يجب ان لا تكون احد الكلمات المستخدمة في التركيب اللغوي للغة بايثون والجدول التالي يبين الكلمات المحجوزة من قبل لغة بايثون:
    and as assert break class continue
    def del elif else except exec
    finally for from global if import
    in is lambda not or pass print raise return try while whith yield
    المثال التالي يلخص القواعد السابقة:
    pass=45
    لاحظ عند استخدامنا لكلمة pass وهي كلمة مججوزة تستخدم في التركيب اللغوي للغة بايثون والتي سوف نتعرف على طريقة استخدامها لاحقا اعطانا مفسر بايثون رسالة بوجود خطأ في التركيب اللغوي
    تمرين: اسند القيمة 100 للمتغير n. في سطر ثاني قم بتغير قيمة n الى القيمة 50. في سطر ثالث حاول ان تعرف القيمة النهائية للمتغير n ؟
    n=100 n=50 n

    تدوين الملاحظات

    تسمح لغة بايثون كغيرها من لغات البرمجة الاخرى بان يكتب المبرمج ملاحظاته داخل الكود البرمجي من اجل ان تساعدة على تذكر وظيفة الكود البرمجي او من اجل اعطاء شروحات وافيه للمبرمجين الاخرين الذين قد يعملون على صيانة وتطوير الكود البرمجي في المستقبل. ويمكن كتابة ملاحظة من سطر واحد في اي مكان من الكود البرمجي ولكن بعد ان يسبق الملاحظة علامة هاش تاق (#). فعند كتابة علامة (#) في بداية السطر فان بايثون يعتبر جميع محتويات هذا السطر ملاحظات للمستخدم وليست كود برمجي كما موضح في المثال التالي:
    # ملاحظات وتعليقات على الكود تكتب هنا x=10
    كما يمكن للمبرمج ان يدون ملاحظاته باستخدام علامة (#) بعد ان كتب سطر من الكود البرمجي كما في المثال التالي:
    x=10 # ملاحظات وتعليقات على الكود البرمجي تكتب هنا
    فمفسر بايثون هنا يتعامل مع الجزء الذي يسبق علامة (#) على انه كود برمجي يتحاج الى تفسير والجزء الذي يقع بعد علامة (#) يعتبره ملاحظات وتعليقات للمبرمج لاتحتاج الى تفسير فيتجاهلها.
    كما يمكن كتابة ملاحظة متعددة السطور باستخادام علامة التنصيص الثلاثية كما في المثال التالي:
    """ تعليقات وملاحظات تعليقات وملاحظات تعليقات وملاحظات """ x=10
    وسوف نتحدث بشىئ من التفصيل عن انواع علامات التنصيص واستخداماتها ولكن ليس الان بل عند بدء الحديث عن البيانات النصية.

    الطباعة على شاشة الكمبيوتر

    ان من المعتاد عند تعلم اي لغة برمجة جديدة ان يتم البدء بتعلم كيفية الطباعة على شاشة الكمبيوتر و ذلك لانها تعتبر وظيفة اساسية في جميع لغات البرمجة.ونحن سوف نسير على هذا العرف هنا على الرغم من تأخيرنا له قليلا. فامر الطباعة على الشاشة في بايثون يكون باسيتخدام الدالة ()print متبوعا بما يراد طباعته داخل قوسي الدالة. وبما اننا في بداية المشوار ولم نتطرق لمفوهم المتغيرات فسوف نبدأ بطباعة قيم رقمية اولا. فالسطر البرمجي التالي يقوم بطباعة القيمة 5 على الشاشة:
    print(5)
    تمرين: قم باسناد القيمة 25 للمتغير m وفي سطر ثاني قم بطباعة قمية m؟
    m=25 print(m)
    25

    الاحرف الكبيرة و الاحرف الصغيرة

    لغتنا العربية الجميلة لاتحوي على مفهوم الحروف الصغيرة والكبيرة بعكس ما هو موجود في اللغة الانجليزية. وبما ان لغة بايثون مكتوبة باللغة الانجليزية فان هذه اللغة تهتم بما اذا كان الكود البرمجي او جزء منه مكتوب بالاحرف الصغيرة او الكبيرة. فالمتغير b يتعامل معه مفسر بايثون على انه مختلف عن B. فعندما نسند القيمة 5 مثلا للمتغير b فان اسناد القيمة 10 للمتغير B لا يغير من قيمة b لان مفسر بايثون يتعامل معهما على انهما متغيرين مختلفين كما في المثال التالي:
    b=5 B=10 print(b) print(B)
    5 10

    العمليات الحسابية

    اجراء العمليات الحسابية الاساسية

    كما هو المعتاد مع لغات البرمجة الاخرى فان العمليات الحسابية الاساسية يمكن القيام بها من اليسار الي اليمين باستخدام الرموز الاتية:

    اولاً: تسخدم العلامة "+" لاجراء عمليات الجمع
    مثال:
    3+4
    ثانياً: تسخدم علامة "-" (الشرطة الاعتيادية وليست السفلية) لاجراء عمليات الطرح
    مثال:
    7-5
    ثالثاً: استخدام علامة النجمة "*" لاجراء عمليات الضرب
    مثال:
    2*3
    رابعاً: تستخدم الشرطة المائلة "/" لاجراء عمليات القسمة
    مثال:
    5/3
    خامساً: تستخدم النجمتين "**" لرفع قيمة عدد لقوة معينه
    مثال:
    3**2
    9
    في المثال السابق تم رفع قيمة العدد 3 للقوة او الاس 2
    سادساً: تستخدم الشرطتين المائلتين "//" لحساب ناتج القسمة بعد اهمال الباقي
    مثال:
    11%3
    2
    سابعاً: تستخدم علامة النسبة المئوية "%" لحساب باقي القسمة
    مثال:
    7%3
    1
    بالنسبة لعملية القسمة فان هناك اختلاف بسيط بين اصدارة بايثون 2 و 3 يجب التنبه عليه. فعند اجراء عملية القسمة في اصدارة بايثون 2 على اعداد طبيعية فان ناتج القسمة يكون عدد طبيعي. بمعنى انه اذا اردنا قسمة العدد 3 على العدد 2 مثلا فان ناتج القسمة يكون 1 وليس 1.5 هذه المشكله غير موجوده في اصدارة بايثون 3. ولتصحيح هذه المشكلة لمن يستخدمون الاصدار 2 يمكنهم استخدام الاعداد الصحيحة في عملية القسمة سواء في البسط او المقام او كليهما.

    قواعد الاسبقية في تنفيذ العمليات الحسابية

    كما هو متعارف عليه في علم الرياضيات فان عمليتي القسمة والضرب تسبق كلا من عمليتي الطرح والجمع وعملية الاس تسبق عمليتي الضرب والجمع الا اذا استخدمت الاقواس لتحديد اسبقية العمليات الحسابية. وهذه امثلة توضح هذا المفهوم:
    3+4*2
    لاحظ ان عملية الضرب تم القيام بها قبل اجراء عملية الجمع لذلك تم الحصول على الناتج 11 بدلا من 14
    10/2-1
    10**2/5+4
    (3+4)*2
    15/(6-3)

    أنواع البيانات

    تنقسم البيانات الأساسية في لغة بايثون الي ثلاثة اقسام رئيسية وهي البيانات العددية والبيانات النصية والبيانات المنطقية. وسوف نقوم في هذه الجزئية من الفصل بإعطاء شيء من التفصيل عن كل نوع من هذه الانواع:

    البيانات العددية

    تنقسم البيانات العددية الى ثلاثة اقسام رئيسية:
    اولاَ: الاعداد الصحيحة (Integers) او بشكل مختصر int وهي الاعداد التي تعبر عن عدم التجزئة وتشمل الاعداد الموجبة والسالبة والصفر { 3 , 2 , 1 , 0 , 1- , 2- , 3- }
    ثانياً: الأعداد العائمة (float) او العشرية وهي الاعداد التي تحتوي على فاصلة عشرية سواء كانت موجبة او سالبة. يجب ملاحظة ان العدد 5 يعتبر عدد صحيح integer وبمجرد وضع فاصلة عشرية بعد هذا الرقم ليصبح .5 فان العدد الان يعتبر عدد عشري float وليس عدد صحيح ويمكن التأكد من ذلك من خلال استخدام الدالة ()type كما في المثال التالي:
    type(5)
    type(5.)
    ثالثاً: الاعداد المركبة (complex numbers) وهي الاعداد التي تحتوي على اعداد حقيقية واعداد خيالية فمثلا العدد 2+3j يعتبر عدد مركب يتكون من جزء صحيح وهو العدد 2 وجزء خيالي هو العدد 3 ويمثل الحرف j الجذر التربيعي للعدد -1 حيث يكتب في لغة بايثون بالطريقة التالية:
    2+3j
    type(2+3j)

    البيانات المنطقية

    البيانات المنطقية هي البيانات التي تحوى احدى القيمتين صح True أو خطأ False . لاحظ ان هاتين القيمتين تبتدأ بحرف كبير في لغة بايثون. وهناك عمليات مقارنة كثير تنتج عنها احدى هاتين القيمتين. فعملية مقارنة التساوي بين قيمتين في بايثون تستخدم علامتي يساوي متتابعتين "==" فعند مقارنة عددين مثلا فاننا نكتبهما بالشكل التالي:
    5==4
    لاحظ ان عدم تساوي القيمتين في المثال السابق ادى الى حصولنا على القيمة False اي خطأ. اما اذا كانت القيمتين متساويتين فان الناتج يكون "True" اي صحيح كما في المثال التالي:
    15==15
    يجب الاشارة الى ان القيمة المنطقية True تمثل 1 في الاعداد الصحيحة. والقيمة المنطقية False تمثل 0 في الاعداد الصحية ويمكن التأكد من ذلك من خلال المثال التالي:
    True==1
    False==0

    البيانات النصية

    كل ما يكتب بين علامتى تنصيص سواء كانت احادية ('z') او ثنائية ("z") او ثلاثية ('''z''') يطلق عليه بيان نصي string أو بشكل مختصر str. فالقيمة "123" يتعامل معها بايثون على انها سلسلة من الرموز النصية الرقمية مختلفة عن القيمة العددية 123. ويمكن التأكد من ذلك باستخدام الدالة ()type كما في المثال التالي:
    x="123" type(x)
    "123"==123
    وهناك عدة قواعد يجب اتباعها عند كتابة البيانات النصيية والتي يمكن تلخيصها في النقاط التالية:
    اولاً: علامتي التنصيص لكل بيان نصي يجب ان تكون من نفس النوع. فعند البدء بعلامة تنصيص احادية يجب ان ينتهي البيان النصي بعلامة تنصيص احادية وكذلك الحال مع علامة التنصيص الثنائية والثلاثية كما في المثال التالي:
    student_name='Ahmad' student_name="Ali" student_name='''Hassan'''
    لاحظ انه عندما نقوم باستخدام علامتي تنصيص مختلفة فان ذلك سوف يجعل مفسر لغة بايثون يظهر رسالة تفيد بوجود خطأ كما في المثال التالي:
    'welcome to python"
    ثانياً: علامة التنصيص الاحادية والثنائية تستخدم لكاتبة نصوص من سطر واحد بينما علامة التنصيص الثلاثية تسمح بطباعة اكثر من سطر. فعند استخدام علامة التنصيص الاحادية والثنائية لطباعة بيان نصي متعدد السطور فان مفسر بايثون يظهر لنا رسالة تفيد بوجود خطأ كما في المثالين التاليين:
    print('welcome to python')
    print("welcome to python")
    اما عند استخدام علامة التنصيص الثلاثية فان مفسر بايثون يتم عملية الطباعة بدون اظهار اي رسالة خطأ كما في المثال التالي:
    print('''welcome to python ''')
    ثالثاً: يمكن استخدام رمز التجاهل "\" في نهاية كل سطر لتمكين علامة التنصيص الاحادية والثنائية من طباعة اكثر من سطر على الشاشة. لكن هناك فرق بين الطريقتين فعلامة التنصيص الثلاثية تبقي البيان النصي كما كتب من ناحية تعدد السطور بينما استخدام رمز التجاهل يقوم بكتابة البيان النصي كسطر واحد كما في المثالين التاليين:
    print ('welcome \ to \ python') print("welcome \ to \ python")
    print('''welcome to python''')
    رابعاً: يمكن استخدام علامة تنصيص او اكثر داخل علامتي تنصيص اخرى ولكن بعد التأكد من ان علامة التنصيص الداخلية مختلفة عن علامة التنصيص المستخدمة في بداية ونهاية النص.
    print('They "like" python') print("My brother's car is quite old")
    خامساً: يمكن استخدام رمز التجاهل المشار اليه سابقا لاداء نفس الوظيفة اذا كانت علامة التنصيص الداخلية مشابهة لعلامتي التنصيص الخارجية. كما ماهو موضح في الامثلة التالية:
    print('They \'like\' python') print('My brother\'s car is quite old')
    وهناك علامات تجاهل أخرى خاصة تؤدي وظائف خاصة فمثلا t\ يعتبرها مفسر بايثون على انها حقل فارغ مكون من اربع مسافات كما في المثال التالي:
    print("1\t2\t3\t4")
    فمفسر بايثون في المثال السابق قام بتحويل كل زمر تجاهل t\ الى حقل فارغ مكون من اربع مسافات. وكذلك الامر مع رمز التجاهل n\ فانه يخبر مفسر بايثون بالنزول الى سطر جديد كما في المثال التالي:
    print("1\n2\n3\n4")
    اما اذا اردنا ان نخبر مفسر بايثون بأن يتجاهل جميع رموز التجاهل الموجودة في النص فإننا نسبق النص بالحرف r ليخبر مفسر بايثون ان هذا النص لا يحتاج الى تفسير الرموز بداخله كما في المثال التالي:
    print(r"1\t2\t3\t4")
    كما يجب الإشارة الى ان البيانات النصية ليس لها حد في قيمها فممكن ان تتكون من سلسلة من الحروف والأرقام والرموز بقدر ما تستوعبه ذاكرة كمبيوتر المستخدم. ويكمن كذلك ان يكون البيان النصي فارغاً كما في المثال التالي:
    x=""
    يجب الاشارة الى انه في معظم لغات البرمجة المعروفه لا يمكن استخدام المتغيرات الا بعد تعريفها مسبقا وذلك بتحديد نوع البيانات التي تشير اليه هذه المتغيرات. لكن الامر مختلف تماما في لغة بايثون. فالمبرمج لا يحتاج الى تعريف المتغيرات قبل استخدامها كما تعلمنا سابقا عن كيفية اسناد قيمة لمتغير. لذلك يطلق على لغة بايثون بانها ديناميكية لانها تقوم بتحديد نوع المتغيرات ذاتيا من خلال التعرف على نوع البيانات المستخدمة مع كل متغير. وهذه الخاصية تعطي المبرمج بلغة بايثون سهوله وسرعة غير مسبوقة في كتابة الاكواد البرمجية. والدالة ()type التي استخدمناها سابقا كفيلة بتوضيح هذه النقطة من خلال المثال التالي:
    x=5 type(x)

    ترك مسافات

    في حين ان لغات البرمجة الاخرى تستخدم مصطلحات واقواس لتحديد بداية ونهاية الاجزاء الداخلية للكود البرمجي فان لغة بايثون تتبع نظام ترك المسافات عند بداية كتابة السطر البرمجي لاداء نفس المهمة. فلغة جافا تستخدم الاقواس لتحديد جزئية الكود الداخلي و علاقته ببقية الاجزاء كما في المثال التالي:
    public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World"); } }
    بينما لغة بايثون تعمد الى ترك مسافة عند بداية كتابة الجزء الداخلى للكود لتجديد مداه وعلاقتة بالاجزاء الاخرى كما في المثال التالي:
    for i in [1,2,3,4]: print (i)
    ليس من المهم ان تفهم وظيفة الكود البرمجي السابق الان لاننا سوف نتطرق اليه في وقت لاحق ولكن المهم ان تعرف ان لغة بايثون تهتم بترك مسافات عند بداية كتابة الاسطر البرمجية لتحديد الاجزاء الداخلية من الكود.فعند كتابة برنامج من سطر واحد مثلا فان ترك اي مسافة قبل بداية السطر البرمجي يجعل مفسر بايثون يرفض التركيب اللغوي و يظهر رسالة تبين سبب المشكلة هو ترك مسافة عند بداية كتابة السطر البرمجي في موضع لايستدعي ترك اي مسافة. لكن ترك مسافة في اي موضع اخى من السطر ليس له اي تأثير على التركيب اللغوي. كما يجب الاشارة الى ان ترك اسطر فارغة بين اسطر الكود البرمجي ليس له اي تأثير يذكر ايضا. سوف نعاود الحديث عن ترك المسافات عندما نبدأ الحديث عن الحلقات التكرارية والدوال في بايثون حيث تستدعى الحاجة للحديث عن ترك مسافات عند كتابة هذه التراكيب اللغوية.

    ملخص الفصل


    اسئلة استيعابية

    1- ما هو اقصى عدد من الاحرف والأرقام يمكن استخدامه في كتابة اسماء المتغيرات؟

    2- صح ام خطأ؟ لا يفرق مفسر بايثون في تعامله مع الكود البرمجي بين الاحرف الكبيرة والصغيرة.

    3-كيف تكتب في بايثون العملية الرياضية x مرفوعا للقوة y؟
    4-ماهو ناتج العملية الرياضية 3%22؟
    5-ماهو ناتج العملية الرياضية 3**1*3؟
    6-اي من الاسماء التالية يمكن استخدامه كاسم متغير ["in","it","on","__init"]؟


    الفصل الثالث : التعامل مع البيانات التجميعية
    (Collection Data)


    في الفصل السابق تعلمنا كيفية التعامل مع البيانات الأساسية وكان كل بيان من تلك البيانات يمثل معلومة مستقلة بذاتها. اما في هذا الفصل فسوف نوسع مفهوم البيانات الى نطاق أوسع بحيث يتم ترتيب هذه البيانات الأساسية بطريقة خاصة في مجموعات فتصبح هذه البيانات مستقلة بذاتها.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. التعرف على البيانات التجمعية وأنواعها الرئيسية.
    2. معرفة التعامل مع الانواع الأساسية للبيانات التجميعية.
    3. التمييز بين الانواع الأساسية للبيانات التجميعية من خلال معرفة خصائص كل نوع.
  • التوسع في معرفة البيانات النصية وكيفية التعامل معها.
  • تعريف البيانات التحميعية

    المقصود بالبيانات التجميعية في لغة بايثون هي بيانات أساسية (سواء كانت عددية او نصية او منطقية) اندرجت تحت مجموعة لها خصائص معينه ومحددة فأصبحت بيانات مستقلة بذاتها يتعامل معها مفسر بايثون بطريقة خاصة. وتنقسم هذه البيانات التجميعية الى أربعة أنواع رئيسية:
    1. القوائم (lists)
    2. الصفوف (tuples)
    3. القواميس (dictionaries)
    4. المجموعات (sets)

    القوائم

    القوائم في لغة بايثون هي عبارة عن مجموعة من البيانات التي يشار اليها بمتغير واحد. وهي تعتبر وسيلة سهلة لتخزين البيانات قبل معالجتها اوتحليلها. ببساطة هي سلسلة من البيانات توضع بين قوسين مربعين يفصل بين كل بيان واخر بفاصلة كما في المثال التالي:
    months=['Jan','Feb','Mar','Apr','Jun','jul','Aug','Sep','Oct','Nov','Dec']
    كما يمكن انشاء قائمة فارغة باكثر من طريقة. فمثلا يمكن انشاء قائمة فارغة باستخدام قوسين مربعين فارغين بعد اسم المتغير كما في المثال التالي:
    list1=[]
    كما يمكن انشاء قائمة فارغة باستخدام الدالة ()list كما في المثال التالي:
    mylist=list()
    لاضافة بيانات لقائمة قارغة او قائمة تحتوي على بيانات سابقة يمكن استخدام الامر ()append بحيث يكون البيان المضاف دائما في اخر القائمة كما في المثال التالي:
    list1=[] list1.append('Sunday') list1.append('Monday') list1.append('Tuesday') list1
    لاحظ استخدام النقطة بعد اسم القائمة لتنفيذ الامر. فهي تعنى قم باضافة القيمة "Sunday" مثلا لللقائمة list1.
    كما يجب التنويه الى ان القوائم يمكن ان تحتوي على اكثر من نوع من البيانات فالقائمة التالية تحتوي على بيانات نصية وبيانات عددية وبيانات منطقية:
    list2=['Ahmad',1990,True,23.5]
    يتم الاشارة الى مكان البيانات في القائمة بارقام صحيحة تسمى Index تبدا من الصفر. فالقائمة السابقة يشار الى القيمة الاولى فيها بالرقم 0 والقيمة الثانية بالرقم 1 وهكذا الى نهاية القيم في القائمة.
    مؤشر القيمة 0 1 2 3 القيمة "Ahmad 1990 True 23.5
    وللحصول على قيمة معينة من قائمة فاننا نقوم بكتابة اسم القائمة متبوعا بقوسين مربعين يوضع بينها رقم مؤشر القيمة. فعلى سبيل المثال عندما نريد القيمة الاولى من قائمة list2 السابقة فاننا نكتب الكود التالي:
    list2[0]
    وعندما نريد القيمة الثالثة مثلا فاننا نكتب الامر التالي:
    list2[2]
    كما يمكن الاشارة الى القيم في قائمة من اليمين الى اليسار او بمعنى اخر من اخر قيمة في القائمة. وللقيام بذلك نقوم باستخدم مؤشر سالب بين القوسين المربعين كما هو موضح في الجدول التالي:
    مؤشر القيمة -4 -3 -2 -1 القيمة "Ahmad 1990 True 23.5
    فللحصول على أخر قيمة في القائمة list2 السابقة نكتب الامر التالي:
    list2[-1]
    للحصول على القيمة قبل الاخيره في قائمة فاننا نستخدم الموشر 2- داخل القوسين المربعين و 3- للقيمة التي بعدها وهكذا لباقي القيم في القائمة.
    كما يمكننا ان نغير قيم البيانات داخل قائمة باستخدام معامل الاسناد (=) كما في المثال التالي:
    Ahmad=5 list2=[Ahmad,1990,True,23.5] list2[0]="Omar" list2
    ولمعرفة مؤشر بيان ما داخل قائمة فإننا نستخدم الدالة ()index بحيث نضع البيان المراد معرفة مؤشره داخل قوسي الدالة كما في المثال التالي:
    list2=["Ahmad",1990,True,23.5] list2.index(1990)
    ولمعرفة عدد البيانات في قائمة فإننا نستخدم الدالة ()len كما في المثال التالي:
    list2=['Ahmad', 1990, 'Hello', True, 23.5] len(list2)
    في مثال سابق تعلمنا كيف نضيف بيان الى قائمة باستخدام الامر ()append وعلمنا ان هذا الامر يقوم بإضافة البيان الى اخر القائمة. لكن ماذا لو أردنا ان نضيف بياناً في مكان معين من قائمة. لأداء هذه المهمة نستخدم الامر ()insert وكيفية استخدام هذا الامر يتطلب أولا ادخال مؤشر البيان داخل القائمة التي سوف يحتلها البيان المراد إدخاله ومن ثم قيمة البيان كما هو موضح في المثال التالي:
    list2=['Ahmad', 1990, True, 23.5] list2.insert(2,"Hello") list2
    ولإزالة بيان من قائمة فان هناك عدة دوال تساعدنا على القيام بذلك بطرق مختلفة. فعندما نريد إزالة اخر بيان في قائمة فإننا نستخدم الدالة ()pop كما في المثال التالي:
    list2=['Ahmad', 1990, 'Hello', True, 23.5] item_removed=list2.pop() list2
    item_removed
    هنا الدالة ()pop قامت بحذف اخر بيان من القائمة وارجعت البيان الذي تم حذفه بحيث اصبح بإمكاننا اسناد ما تم حذفه الى متغير كما فعلنا في المثال السابق
    وكذلك يمكن إزالة عنصر معين من القائمة باستخدام الدالة ()remove وذلك بتحديد البيان المراد حذفه كما في المثال التالي:
    list2=['Ahmad', 1990, 'Hello'] item_removed=list2.remove("Ahmad") list2
    item_removed
    لاحظ ان الدالة ()remove لم تقم بارجاع البيان الذي تم حذفه كما هو الحال مع الدلة ()pop.
    كما انه بالإمكان استخدام الامر del لإزالة البيان المراد كما في المثال التالي:
    list2=['Ahmad', 1990, 'Hello', True, 23.5] del list2[1] list2
    وأخيرا يمكن افراغ القائمة من البيانات بالكامل باستخدام الدالة ()clear بعد اسم القائمة كما في المثال التالي:
    list2=['Ahmad', 'Hello', True, 23.5] list2.clear() list2
    يجب ملاحظة ان القائمة تسمح بتكرار العناصر بمعنى انه يمكن تخزين نفس البيان في قائمة اكثر من مرة كما في المثال التالي:
    greatings=["hello","hello","hello"] greatings
    ولمعرفة عدد العناصر المكررة في قائمة نستخدم الدالة ()count بعد تحديد البيان المراد معرفة عدد مرات تكراره كما في المثال التالي:
    greatings=['hello', 'hello', 'hello', 'Hi', 'Hi'] greatings.count("hello")
    greatings=['hello', 'hello', 'hello', 'Hi', 'Hi'] greatings.count("Hi")
    greatings=['hello', 'hello', 'hello', 'Hi', 'Hi'] greatings.count("bye")
    في مثال سابق تعلمنا كيف نستخدم الدالة ()append لإضافة بيان واحد الى اخر القائمة. ولان نريد ان نوسع معرفتنا بهذه الدالة ونتعرف على ان هذه الدالة باستطاعتها إضافة قائمة الى اخر القائمة بحيث تظهر القائمة كقائمة جزئية داخل قائمة رئيسية كما في المثال التالي:
    numbers=[1,2,3,4] numbers.append(['a','b','c']) numbers
    لاحظ ان القائمة الرئيسية هي القائمة التي تسبق ()append والقائمة الفرعية هي التي بداخل قوسي ()append. لكن ماذا لو أردنا ان ندمج عناصر القائمة الفرعية مع عناصر القائمة الرئيسية بحيث تصبح كأنها قائمة رئيسية واحدة. للقيام بذلك يمكننا استخدام علامة الجمع بين القائمتين كما في المثال التالي:
    numbers=[1, 2, 3, 4] list3=numbers+['a','b','c'] list3
    numbers=[1, 2, 3, 4] list3=['a','b','c']+numbers list3
    لاحظ ان ترتيب البيانات يعتمد على الطريقة التي تمت بها عملية الجمع السابقة.
    كما يمكننا ان نستخدم الدالة ()extend لأداء نفس المهمة بحيث نضع القائمة الفرعية المراد دمجها داخل قوسي الدالة كما في المثال التالي:
    numbers=[1,2,3,4] numbers.extend(['a','b','c']) numbers
    يمكن اجراء عملية ضرب على قائمة فتكون النتيجة كالتالي:
    [1,2,3]*4
    لاحظ ان عملية الضرب هنا هي مجرد تكرار للبيانات داخل القائمة.
    ويمكن عكس ترتيب البيانات في قائمة باستخدام الدالة ()reverse كما في المثال التالي:
    numbers=[1, 2, 3, 4, 'a', 'b', 'c'] numbers.reverse() numbers
    ولإعادة ترتيب البيانات داخل قائمة فإننا نستخدم الدالة ()sort بحيث تقوم هذه الدالة بترتيب البيانات تصاعديا بشكل تلقائي على حسب ترتيب الحروف الابجدية او الاعداد كما في المثال التالي:
    cars = ['Ford', 'BMW', 'Volvo'] cars.sort() cars
    لاحظ ان في المثال السابق تم اعادة ترتيب البيانات على نفس القائمة الاصلية cars. ولاجراء عملية اعادة الترتيب في قائمة جديدة بحيث تبقى القائمة الاصلية كما هي نستخدم الدالة ()sorted كما في المثال التالي:
    cars=["Ford","BMW","Volvo"] cars2=sorted(cars) print('Original list: ',cars) print('Sorted list: ',cars2)
    ولعكس الترتيب بحيث يكون تنازليا نستخدم الخيار reverse=True داخل قوسي الدالتين السابقتين كما في المثالين التاليين:
    cars = ['Ford', 'BMW', 'Volvo'] cars.sort(reverse=True) cars
    cars = ['Ford', 'BMW', 'Volvo'] cars2=sorted(cars,reverse=True) cars2
    ويمكن أيضا تحدد طريقة الترتيب بحسب الكيفية التي نرغب بها ولكن هذه الخاصية متقدمة قليلا ولا نستطيع الحديث عنها هنا الان.
    ولمعرفة ما اذا كان بيان معين موجود في قائمة فإننا نستخدم in بين البيان المراد البحث عنه واسم القائمة ليكون معنى التركيب اللغوي "هل البيان x موجود في القائمة y" فيكون ناتج العملية اما بصح True او خطأ False كما في المثال التالي:
    colors=['green','red','blue'] 'green' in colors
    colors=['green','red','blue'] 'yellow' in colors
    ولعمل نسخه من القائمة نستخدم الدالة ()copy كما في المثال التالي:
    cars = ['Ford', 'BMW', 'Volvo'] cars2=cars.copy() cars2
    قد يتبادر الى الاذهان ان عملية النسخ باستخدام الدالة ()copy غير ضرورية لان اجراء عملية اسناد قائمة لمتغير جديد سوف تفي بالغرض كما في المثال التالي:
    cars = ['Ford', 'BMW', 'Volvo'] cars2=cars cars2
    لكن في حقيقة الامر هناك اختلاف جوهري بين استخدام الدالة ()copy وعملية الاسناد. فعملية الاسناد لا تعني تكوين قائمة مستقلة عن القائمة الاولى. بل ان اجراء أي تغيير في أي متغير سوف يؤثر على القائمة الاخرى كما في المثال التالي:
    cars = ['Ford', 'BMW', 'Volvo'] cars2=cars # عملية الاسناد الى متغير جديد cars.append("GMC") print(cars) print(cars2)
    لاحظ انه عند إضافة بيان الى القائمة cars تم احداث نفس التغيير في المتغير cars2 ايضاً. وذلك لأنه في حقيقة الامر لا يوجد قائمتين في ذاكرة الكمبيوتر أصلا. ان ذاكرة الكمبيوتر تحتفظ بقائمة واحدة فقط وعملية الاسناد قامت فقط بتكوين متغير يشير الى نفس القائمة كما في الشكل التالي:

    اما عملية النسخ بالدالة ()copy فانها تقوم بإنشاء قائمة مستقلة عن القائمة الاولى في داكرة الكمبيوتر كما في الشكل التالي:

    اعادة كتابة المثال السابق باستخدام الدالة ()copy يثبت صحة هذه المعلومة كما يلي:
    cars = ['Ford', 'BMW', 'Volvo'] cars2=cars.copy() # عمل نسخة من القائمة cars.append("GMC") print(cars) print(cars2)
    لذلك عند اجراء تغيير على القائمة التي نسخت بالدالة ()copy فان هذا التغيير لا يؤثر على القائمة الاصلية والعكس صحيح اي ان التغيير في القائمة الاصلية بعد عملية النسخ فانه لا يؤثر على القائمة المنسوخة كما في المثال التالي:
    cars = ['Ford', 'BMW', 'Volvo'] cars2=cars.copy() # عملية النسخ cars.append([1,2,3]) # عملية التغيير على القائمة الاصلية print(cars) print(cars2)
    ويمكن الحصول على نفس وظيفة النسخ بالدالة ()copy باستخدام دالة تعريف القائمة ()list التي ذكرناها في بداية الفصل كما في المثال التالي:
    numbers=[1,2,3,4] numbers2=list(numbers) numbers2.pop() print(numbers) print(numbers2)
    ويمكن تجزئة قائمة (list slicing) الى قوائم أصغر بعدة طرق. فمثلا اذا اردنا اخذ الثلاثة البيانات الاولى من القائمة
    [ ‘a’ , ’b’ , ’c’ , ’d’ , ’e’ , ’f’’ , ’g’ ] فإننا نقوم بالتالي:
    letters=['a','b','c','d','e','f','g'] letters[0:3]
    ما قمنا به في المثال السابق هو اننا استخدمنا مؤشر البيان الذي نريد ان تبتدأ به القائمة الجزئية داخل قوسين مربعين ثم وضعنا نقطتين فوق بعض (:) ثم وضعنا مؤشر اخر بيان لا نريد ان يكون في القائمة الجزئية. ففي المثال السابق اول بيان نريده في القائمة الجزئية هو ‘a’ ومؤشره 0 واخر بيان لا نريده ان يكون في القائمة الجزئية هو ‘d’ ومؤشره هو 3 . لذلك حصلنا على القائمة الجزئية [ ‘a’ , ’b’ , ’c’ ] . ويكمن الحصول على نفس المهمة بترك مؤشر البيان الذي سوف تبتدأ به القائمة الجزئية فارغ لأن مفسر بايثون سوف يفترض انك تريد ان تبتدأ القائمة الجزئية من بداية القائمة الرئيسية كما في المثال التالي:
    letters=['a','b','c','d','e','f','g'] letters[:3]
    وللحصول على قائمة جزئية تبتدأ مثلاً من الحرف ‘e’ وتنتهي باخر بيان في القائمة السابقة نكتفي بتحديد مؤشر العدد الذي نريد ان تبتدأ به القائمة الجزئية ونترك المؤشر الاخر فارغ لأن مفسر بايثون يفترض انك تريد بقية البيانات في القائمة الرئيسية كما في المثال التالي:
    letters=['a','b','c','d','e','f','g'] letters[4:]
    كما يمكن فك القائمة الى متغيرات اعتيادية باسخدام متغير واحد لكل بيان موجود في القائمة كما في المثال التالي:
    numbers=[1,2,3] a,b,c=numbers print(a) print(b) print(c)
    في حال استخدامنا لمتغيرات اقل او اكثر من عدد البيانات الموجودة في القائمة فان مفسر بايثون يعطينا رسالة بوجود خطا في الكود البرمجي يفيد بأن عدد القيم في القائمة اكثر او اقل من عدد المتغيرات المعطى كما في المثاليين التاليين:
    numbers=[1,2,3] a,b=numbers
    numbers=[1,2] a,b,c = numbers
    يمكن تلافي اخطاء الفك السابقة باستخدام علامة النجمة قبل المتغير الاخير كما في المثال التالي:
    numbers=[1,2,3] a,*b=numbers print(a) print(b)
    على الرغم من ان عدد البيانات في القائمة اكثر من عدد المتغيرات المعطاه في المثال السابق الا ان استخدام النجمة قبل المتغير b اعطى تعليمات لمفسر بايثون بان يسند كل ما تبقى من بيانات في القائمة للمتغير b فتكون لدينا قائمة مكونه عنصرين اسندت للتمتغير b بينما تم فك البيان الاول وتم اسناده للمتغير a.
    في الحالة التي يكون فيها عدد المتغيرات اكثر من عدد البيانات الموجودة في قائمة فان استخدام النجمة مع المتغير c يعطى تعليمات الى مفسر بايثون بان يضع كل ما تبقى من بيانات في هذا المتغير. وبما ان عمليه الفك لا تبقى شي للمتغير c فان مفسر بايثون سوف يسند للمغير c قائمة فارغة كما في المثال التالي:
    numbers=[1,2] a,b,*c=numbers print(a) print(b) print(c)

    أنشطة استكشافية

    ١-استكشف ماذا ينتج عن استخدام [:]letters للقائمة السابقة؟
    letters[-2]
    ٢- استكشف ماذا ينتج عن استخدام letters[2:-2] للقائمة السابقة؟
    letters[2:-2]
    ٣-استكشف ماذا ينتج عن استخدام [::]letters للقائمة السابقة باسناد الناتج الى المتغير letters2 وتعرف على ما اذا كانت عملية التجزئة تقوم بعملية التغيير على نفس القائمة الاصلية ام انها تقوم بعمل نسخة مستقلة عنها؟
    ٤-استكشف ماذا ينتج عن استخدام letters[::-1] للقائمة السابقة؟ حاول تتذكر أي دالة من الدوال التي تعلمناها سابقا تقوم بنفس المهمة؟
    letters[::-1]
    ٥-استكشف ماذا ينتج عن استخدام letters[::1] للقائمة السابقة؟
    letters[::1]
    ٦-استكشف ماذا ينتج عن استخدام letters[::2] للقائمة السابقة؟
    7-استكشف كيف يتم فك القائمة السابقة الى ثلاث متغيرات المتغير الاول يشير القيمة الاولى من القائمة والمتغير الثاني يشير الى كل عناصر القائمة ماعد القيمة الاخيرة والمتغير الثالث يشير الى آخر قيمة في القائمة letters[::2] ؟
    8-جرب استخدام الامر ()dir واضعا بين قوسيه اسما لقائمة و تعرف على الدوال التي يمكن استخدامها مع هذه القائمة؟

    الصفوف (tuples)

    الصفوف في لغة بايثون هي عبارة عن مجموعة من البيانات غير قابلة للتغيير توضع بين قوسين منحنيين يفصل بين كل بيان وآخر بفاصلة ويشار اليها بمتغير واحد كما في المثال التالي:
    fruits=("banana","orange","apple") numbers=(1,2,3,4) values=(True,False,False,True)
    حتى لو لم نكتب القوسين المنحنيين عند كتابتنا للبيانات التي تم فصلها عن بعض بفاصله فان مفسر بايثون سوف يفترض انك تريد كتابة صف من البيانات ويظهر لك النتيجة بقوسين منحنيين كما في المثال التالي:
    colors='blue','red','orange' colors
    ويمكن انشاء صف من قائمة باستخدام الدالة ()tuple و قائمة من صف باستخدام الدالة ()list كما في المثالين التاليين:
    colors=["red","yellow","blue"] mytuple=tuple(colors) type(mytuple)
    numbers=(1,2,3,4) mylist=list(numbers) type(mylist)
    كما يجب ملاحظة ان كتابة صف من بيان واحد لا يمكن القيام به الا بعد ان نضيف فاصلة بعد البيان سواء استخدمنا القوسين او لم نستخدمها وذلك لان مفسر بايثون سوف يعتقد ان المبرمج يريد تكوين متغير اعتيادي وليس صف كما في المثال التالي:
    colors="blue" numbers=(1) print(type(colors)) print(type(numbers))
    colors="blue", numbers=(1,) print(type(colors)) print(type(numbers))
    وللحصول على صف فارغ يمكننا ان نستخدم الطريقة التالية:
    houses=()
    وكما هو الحال في القوائم فان البيانات في الصفوف يتم الحصول عليها بتحديد مؤشر البيان داخل قوسين مربعين بعد اسم متغير الصف كما في المثال التالي:
    fruits=("banana","orange","apple") fruits[0]
    fruits[2]
    وكذلك عملية تجزئة الصفوف تتم بنفس الطريقة التي تعلمناها في تجزئة القوائم كما في المثال التالي:
    colors=('red','green','blue','yellow','orange') colors[2:4]
    colors[:4]
    colors[:]
    وكذلك عملية الجمع والضرب بين صفين تتم بنفس الطريقة التي تعلمناها مع القوائم كما في المثال التالي:
    colors=('red','green','blue','yellow','orange') colors+(1,2,3)
    letters=('a','b','c') letters*3
    بشكل مشابه للقوائم يمكن معرفة عدد البيانات في صف باستخدام الدالة len() كما في المثال التالي:
    colors=('red','green','blue','yellow','orange') len(colors)
    وللسؤال عما إذا كان بيان ما موجود في صف فإننا نتبع نفس الطريقة المستخدمة مع القوائم باستخدام in كما في المثال التالي:
    colors=('red','green','blue','yellow','orange') 'blue' in colors
    'pink' in colors
    من خلال ماسبق لاحظت ان الصفوف تشبه القوائم بشكل كبير ولكن هناك فرق جوهري بين الاثنين وهو ان البيانات في الصفوف لا يمكن تغييرها ولو حاولنا ذلك نجد ان مفسر بايثون يعطينا رسالة بوجود خطأ مفاده ان الصفوف غير قابلة للتغيير immutable كما في المثال التالي:
    fruits=("banana","orange","apple") fruits[0]="grapes"
    يتم التعامل مع فك الصفوف الى متغيرات بنفس الطريقة التى تعلمناها فيما يخص القوائم كما في الامثلة التالية:
    numbers=(1,2,3,4) a,*b=numbers print(a) print(b)
    numbers=(1,2) a,b,*c=numbers print(a) print(b) print(c)
    numbers=(1,2,3,4,5,6) a,*b,c=numbers print(a) print(b) print(c)

    أنشطة استكشافية

    ١-حاول ان تستخدم احد الدوال التي استخدمناها مع القوائم لاحداث تغيير على صف كاستخدام الدالة ()remove او ()append مثلا ماذا تلاحظ ولماذا؟
    ٢-حاول ان تستخدم الدوال التي لا تحدث تعديلا على بيانات الصف كالدالة ()count و الدالة ()index ماذا تلاحظ؟
    ٣-جرب استخدام الامر del على صف ماذا تلاحظ؟
    ٤-جرب استخدام الامر ()dir واضعا بين قوسيه اسما لصف و تعرف على الدوال التي يمكن استخدامها مع الصف؟

    القواميس(dictionaries)

    القواميس عبارة عن مجموعة من البيانات توضع بين قوسين متعرجين ويفصل بين كل بيان واخر بفاصلة بحيث يتكون كل بيان من جزأين. الجزء الاول يسمى المفتاح (key) والجزء الاخر يسمى القيمة (value) ويفصل بين المفتاح والقيمة بنقطتين فوق بعض كما في المثال التالي:
    info={'name':'Hassan','Age':22,'Job':'student'}
    المفتاح في القاموس يجب ان يكون مميزا عن غيره من المفاتيح وإذا حاولنا كتابة قاموس يحتوي على مفتاحين متشابهين فان القيمة الاولى سوف يتم حذفها وتبقى القيمة الأخيرة داخل القاموس كما في المثال التالي:
    info={'name':'Ali','Age':22,'Job':'student','name':'Waleed'} info
    اما قيم القاموس فيمكن ان تتشابه ولا يعترض مفسر بايثون على تشابه القيم طالما المفاتيح مختلفة كما في المثال التالي:
    example={1:'odd',2:'even',3:'odd'} example
    مفاتيح القاموس يجب ان تكون بيانات غير قابلة للتغير فيمكن ان تكون نصا او رقما او صفا ولكن لا يمكن ان تكون قائمة كما في المثال التالي:
    keys_types={23:'number','Hello':'string',(1,2,3):'tuples'} keys_types
    list_dict={[1,2]:'list'}
    لاحظ انه عندما استخدمنا القائمة كمفتاح في القاموس اظهر لنا مفسر بايثون رسالة بوجود خطأ من النوع TypeError والذي يفيد بوجود خطأ في نوعية البيانات المستخدمة. ويمكن كتابة قاموس فارغ بإحدى الطريقتين التاليتين:
    info={}
    info2=dict()
    ويمكن الحصول على قيمة لمفتاح ما في قاموس باستخدام قوسين مربعين توضع بعد اسم القاموس ويكتب داخلها اسم المفتاح كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} info['Age']
    info["name"]
    وعند استخدام التركيب اللغوي السابق للحصول على قيمة مفتاح غير موجود في القاموس فان محرر بايثون يعطينا رسالة بوجود خطأ من النوع KeyError كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} info['birth_date']
    ويمكن استخدام الدالة ()get للحصول على قيمة مفتاح في قاموس كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} info.get('name')
    الفرق بين استخدام القوسين المربعين واستخدام الدالة ()get في الحصول على قيمة مفتاح في قاموس هو ان الدالة ()get لا تعطينا رسالة بوجود خطأ عندما يكون المفتاح غير موجود في القاموس كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} info.get('birth_date')
    القواميس مثل القوائم يمكن تعديل البيانات الموجودة بداخلها فمثلا إذا أردنا تعديل قيمة المفتاح “Age” في القاموس السابق فإننا نقوم بالآتي:
    info={'name':'Ahmad','Age':22,'Job':'student'} info['Age']=40 info
    كما يمكن إضافة بيانات جديدة الى قاموس بالطريقة التالية:
    info={'name':'Ahmad','Age':22,'Job':'student'} info['car']='Ford' info
    لمعرفة عدد البيانات في قاموس فاننا نستخدم الدالة ()len كما فعلنا في القوائم والصفوف:
    info={'name':'Ahmad','Age':22,'Job':'student'} len(info)
    ولمعرفة ما اذا كان هناك بيان ما موجود في قاموس فاننا نستخدم in كما تعلمنا سابقا في القوائم والصفوف:
    info={'name':'Ahmad','Age':22,'Job':'student'} 'Age' in info
    'car' in info
    لحذف بيان من قاموس فإننا نستخدم الامر del كما فعلنا في القوائم:
    info={'name':'Ahmad','Age':22,'Job':'student'} del info['Job']
    كما يمكن استخدام الدالة ()pop لاداء نفس المهمة مع امكانية ارجاع قيمة المفتاح المحذوف واضافته لمتغير لاستخدامه في عمليات اخرى كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} removed_item=info.pop("name") print(info)
    removed_item
    كما يمكن استخدام الدالة ()popitem لحذف اخر بيان تم اضافته للقاموس مع امكانية ارجاع المفتاح المحذوف وقيمته لاستخدامهما في عمليات اخرى كما في المثال التالي:
    info={'name':'Ahmad','Age':22,'Job':'student'} removed_item=info.popitem() print(info) print(removed_item)
    ولتفريغ القاموس من البيانات بالكامل نستخدم الدالة ()clear كما في المثال التالي:
    info={'name':'Ahmad','Age':22} info.clear() info
    ولمعرفة كافة المفاتيح في قاموس ما نستخدم الدالة ()keys كما في المثال التالي:
    info={'name': 'Ahmad', 'Age': 22, 'Job': 'student'} info.keys()
    ولمعرفة كافة القيم في القاموس نستخدم الدالة ()values كما في المثال التالي:
    info={'name': 'Ahmad', 'Age': 22, 'Job': 'student'} info.values()
    ولدمج بيانات قاموسين نستخدم الدالة ()update كما في المثال التالي:
    info={'name': 'Ahmad', 'Age': 22, 'Job': 'student'} info2={'title':'Mr','phone':'00000'} info.update(info2) info
    لاحظ ان القاموس الذي يسبق الدالة هو القاموس الأساسي والقاموس الذي بداخل قوسي الدالة هو القاموس الذي تم دمج البيانات منه. يجب ملاحظ ان بالإمكان وضع قاموس فرعي داخل قاموس رئيسي كما يلي:
    info={'name': 'Ahmad', 'Age': 22, 'Job': 'student'} info2={'title':'Mr','phone':'00000'} info['more_data']=info2 info
    في المثال السابق تم إضافة القاموس الفرعي باستخدام مفتاح جديد اسميناه more_data كما تعلمنا سابقا عن كيفية إضافة بيان جديد الى قاموس.
    ويمكن استخدام الدالة ()items لعرض جميع البيانات في قاموس كقائمة كل بيان يظهر كصف مكون من مفتاح وقيمة كما في المثال التالي:
    info={'name': 'Ahmad', 'Age': 22, 'Job': 'student'} info.items()
    كل ما ينطبق على القوائم من عمليات النسخ يمكن تطبيقة على القواميس كما في المثالين التاليين:
    cars={"name":"Ford", "model":2015, "color":"white"} cars2=cars cars2["milage"]=100000 print(cars) print(cars2)
    cars={"name":"Ford", "model":2015, "color":"white"} cars2=cars.copy() cars2["milage"]=100000 print(cars) print(cars2)
    cars={"name":"Ford", "model":2015, "color":"white"} cars2=dict(cars) cars2["milage"]=100000 print(cars) print(cars2)

    تمارين استكشافية

    ١- استكشف ماذا كان عمليات الضرب والجمع التي قمنا بها مع القوائم يمكن استخدامها مع القواميس؟
    ٢-جرب استخدام الامر ()dir واضعا بين قوسيه اسما لقاموس و تعرف على الدوال التي يمكن استخدامها مع القاموس؟
    ٣- استكشف الية عمل الدالة ()popitem على القاموس؟
    ٤- استخدم الدالة ()help واضعاً بين قوسيها اسما لدالة تعمل عملا ما على القاموس محاولاً معرفة كيفية عمل تلك الدالة؟ تلميح: يجب كتابة اسم قاموس مفصولا بنقطة بينه وبين الدالة المراد معرفة معلومات عنها بدون كتابة قوسيها داخل قوسي الدالة ()help.

    المجموعات (sets)

    المجموعات في لغة بايثون هي عبارة عن تجمع لبيانات غير قابلة للتكرار توضع بين قوسين متعرجين {} يفصل بين كل بيان واخر بفاصلة ويشار اليها بمتغير واحد كما في المثال التالي:
    numbers={1,2,3,4,1,3} print(numbers) {1, 2, 3, 4}
    لاحظ في المثال السابق اننا حاولنا تكوين مجموعة بيانات متكررة لكن محرر بايثون حذف البيانات المتكررة من المجموعة لان المجموعات في بايثون لا تسمح بتكرار البيانات المتشابهة. لاحظ أيضا التشابه بين المجموعات والقواميس من حيث الاقواس. الاختلاف بين القواميس والمجموعات يكمن في طريقة كتابة البيانات فالقواميس كل بيان يتكون من مفتاح وقيمة مفصولين بنقطتين فوق بعض اما المجموعات فهي بيانات اعتيادية.
    ويمكن انشاء مجموعة اما بالطريقة التي استخدمناها في المثال السابق او باستخدام الدالة set() وإدخال قائمة من البيانات داخل قوسي الدالة كما في المثال التالي:
    letters=set(['a','b','c']) letters
    واذا اردنا ان ننشئ مجموعة فارغة فإننا لا نستطيع ان نستخدم قوسين فارغين لأن هذا التركيب اللغوي محجوز للقواميس ولكن بوسعنا ان نستخدم الدالة set() بدون ان ندخل أي شيء بين قوسيها كما في المثال التالي:
    numbers={} #استخدام القوسين المتعرجين محجوز للقواميس type(numbers)
    letters=set() letters
    ولإضافة بيان الي مجموعة فارغة او مجموعة محتوية على بيانات سابقة نستخدم الدالة ()add كما في المثال التالي:
    letters=set() letters.add('Ahmad') letters.add('Waleed') letters
    لاحظ ان ترتيب البيانات في المجموعات ليس بالضرورة ان يكون بنفس الترتيب الذي ادخلنا فيه البيانات وذلك لان المجموعات لا تستخدم مؤشر البيانات للإشارة الى البيانات بداخلها واذا حاولنا القيام بذلك فان مفسر بايثون سوف يظهر لنا رسالة تفيد بان المجموعات لا تستخدم مؤشر البيانات كما في المثال التالي:
    letters={'Hassan', 'Ahmad', 'Waleed'} letters[1]
    ولاضافة مجموعة الى مجموعة اخرى نستخدم الدالة ()update كما في المثال التالي:
    set_A={1,2,3,4} set_B={1,2,5,7} set_A.update(set_B) print(set_A)
    لاحظ ان عملية الترتيب في المثال السابق مهمة فالدالة ()update تضيف بيانات المجموعة set_B الى بيانات المجموعة set_A اذا كانت غير موجودة مسبقا فيها.
    ولحذف بيان من مجموعة هناك عدة خيارات لاداء هذه المهمة. فيمكن استخدم الدالة ()remove كما في المثال التالي:
    letters={'Hassan', 'Ahmad', 'Waleed'} letters.remove('Ahmad') letters
    ويمكن ايضا استخدام الدالة ()discard لحذف بيان من مجموعة كما في المثال التالي:
    letters={2, (3, 4), 'Hassan', 'Waleed'} letters.discard(2) letters
    الفرق بين استخدام الدالة ()remove والدالة ()discard هو ان الدالة ()discard لا تعطينا رسالة بوجود خطأ عندما نطلب حذف بيان ليس موجود داخل مجموعة اما الدالة ()remove فسوف تظهر لنا رسالة خطأ KeyError عند عندم وجود البيان داخل المجموعة كما في المثال التالي:
    letters={(3, 4), 'Hassan', 'Waleed'} letters.discard('Ahmad') # لم تظهر لنا أي رسالة رغم ان البيان غير موجود
    letters.remove('Ahmad')
    كما يمكن أيضا استخدام الدالة ()pop لحذف بيان عشوائي من مجموعة كما في المثال التالي:
    letters={(3, 4), 'Hassan', 'Waleed'} letters.pop()
    لاحظ ان البيان الذي يتم حذفه ليس بالضرورة ان يكون في اخر المجموعة كما هو الحال في القوائم وانما يتم اختياره بشكل عشوائي كما حصل في المثال السابق حيث تم حذف بيان من وسط المجموعة وليس من اخرها.
    كما يمكن استخدام الدالة ()clear كما فعلنا بالقوائم والصفوف لحذف جميع البيانات من مجموعة كما في المثال التالي:
    letters={'Hassan', 'Waleed'} letters.clear() letters
    ذكرنا سابقا اننا نستطيع إضافة بيان الى مجموعة وكذلك باستطاعتنا حذفه لكن ليس باستطاعتنا إعادة تعيين قيم البيانات داخل المجموعات كما كنا نفعل بالبيانات داخل القوائم. لذلك يجب ان تكون البيانات المدخلة للمجموعة غير قابلة لإعادة التعيين كالبيانات النصية والصفوف والأرقام. وإذا حاولنا إضافة بيان قابل للتغيير كالقوائم مثلا فان بايثون يظهر لنا رسالة بوجود خطأ كما في المثال التالي:
    letters={'Hassan', 'Waleed'} letters.add(2) letters.add((3,4)) letters
    letters.add([2,4,6])
    ويكمن التعرف على ما اذا كان هناك بيان موجود في مجموعة باستخدام الامر in كما فعلنا سابقا في القوائم والصفوف وهذا مثال عليه هنا:
    set_1={1,2,'a','b'} 1 in set_1
    3 in set_1
    ويمكن اجراء عملية التقاطع بين مجموعتين للحصول على جميع البيانات الموجودة في كلا المجموعتين بعد حذف التكرار باستخدام الدالة ()union كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} set_1.union(set_2)
    اما اذا اردنا ان نحصل على البيانات المتشابهة فقط فاننا نستخدم الدالة ()intersection كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} set_1.intersection(set_2)
    وللحصول على البيانات الموجود في مجموعة شريطة ان هذا البيانات غير موجودة في مجموعة ثانية نستخدم الدالة ()difference كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} set_1.difference(set_2)
    وللحصول على جميع البيانات داخل مجموعتين بعد حذف العناصر المتشابهة نستخدم الدالة ()symmetric_difference كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} set_1.symmetric_difference(set_2)
    لاحظ ان جميع الدوال الاربعة السابقة تقوم بتكون مجموعة جديد لاحداث التغيير لا تغير في المجموعات الاصلية ولتأكد من ذلك يمكن ملاحظة ناتج الامثلة التالية:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.union(set_2) print(result) print(set_1) print(set_2)
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.intersection(set_2) print(result) print(set_1) print(set_2)
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.difference(set_2) print(result) print(set_1) print(set_2)
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.symmetric_difference(set_2) print(result) print(set_1) print(set_2)
    ولاحداث تغيير مباشر في المجموعة الاصلية نستخدم الدوال التالية:
    • ()intersection_update
    • ()difference_update
    • ()symmetric_difference_update
    • بدلا من الدوال السابقة للقيام بهذه المهمة كما في الامثلة التالية:
    set_1={1,2,'a','b'} set_2={'c','d',1,2} set_1.intersection_update(set_2) print(set_1) print(set_2)
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.difference_update(set_2) print(set_1) print(set_2)
    set_1={1,2,'a','b'} set_2={'c','d',1,2} result=set_1.symmetric_difference_update(set_2) print(set_1) print(set_2)
    كما يمكن استخدام الدالة ()issubset للسؤال عن ما اذا كانت مجموعة ما هي مجموعة جزئية من مجموعة أخرى كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={1, 'd', 2, 'c'} set_3={'a','b'} set_3.issubset(set_1)
    set_2.issubset(set_1)
    كما يمكن استخدام الدالة ()issuperset للسؤال عن ما اذا كانت مجموعة ما هي مجموعة مشتملة على جميع بيانات مجموعة أخرى كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={1, 'd', 2, 'c'} set_3={'a','b'} set_1.issuperset(set_3)
    set_2.issuperset(set_1)
    كما يمكن استخدام الدالة ()isdisjoint للسؤال عن ما اذا كانت مجموعة ما منفصلة عن مجموعة أخرى كما في المثال التالي:
    set_1={1,2,'a','b'} set_2={1, 'd', 2, 'c'} set_3={'c','d'} set_1.isdisjoint(set_3)
    set_1.isdisjoint(set_2)
    كل ما ينطبق على القوائم من عمليات النسخ يمكن تطبيقه على المجموعات.
    cars={"Ford", 2015, "white"} cars2=cars cars2.add(100000) print(cars) print(cars2)
    cars={"Ford", 2015, "white"} cars2=cars.copy() cars2.add(100000) print(cars) print(cars2)
    cars={"Ford", 2015, "white"} cars2=set(cars) cars2.add(100000) print(cars) print(cars2)
    وهناك ما يدعى بالمجموعة المجمدة frozenset والتي لا تسمح بتغيير باجراء اي تغيير عليها. فعند محالة اضافة عنصر لهذه المجموعة المجمدة فان مفسر بايثون يعطي رسالة خطأ تفيد بعدم قابلية المجموعة المجمدة للاضافة كما في المثال التالي:
    set_A=frozenset([1,2,4]) type(set_A)
    set_A.add(7)
    set_A.remove(1)

    تمارين استكشافية

    ١- استخدم الدالة ()pop لحذف بيان عشوائي من مجموعة فارغة؟ ماذا تلاحظ؟
    ٢- استكشف إمكانية إضافة مجموعة الى مجموعة ؟
    ٣- استخدم محموعتين للتعرف على الفرق بين استخدم set_1.difference(set_2) واستخدام set_2.difference(set_1) ؟
    ٤- ١ستخدم مجموعتين للتعرف على الفرق بين استخدام الدالة ()union والدالة ()symmetric_difference؟
    ٥- جرب استخدام الامر ()dir واضعا بين قوسيه اسما لمجموعة و تعرف على الدوال التي يمكن استخدامها مع المجموعة؟

    البيانات النصية (String)

    تعرفنا في الفصل الثاني على بعض اساسيات البيانات النصية واجلنا جزء من هذه الأساسيات الى حين التعرف على مفهوم القوائم في بايثون والذي قمنا به في بداية هذا الفصل والان سوف نكمل ما بدأناه من اساسيات البيانات النصية فنتعرف على بعض العمليات التي يمكن اجراؤها عليها وطريقة تجزئتها وتهيئتها للطباعة.
    على الرغم من البيانات النصية هي أحد النوع البيانات الرئيسية في بايثون الا انه يمكن اعتبارها بيانات تجميعية بحيث يمكن التعامل معها بنفس الطريقة التي تعاملنا بها مع القوائم. فيمكن انشاء نص فارغ باستخدام أحد أنواع علامات التنصيص التي تعلمناها سابقا كما في المثال التالي:
    text=''
    text=""
    text=''''''
    ولمعرفة عدد سلسلة الحروف الموجودة في النص “Hello python” نقوم باستخدام الدالة ()len كما في المثال التالي:
    len("Hello python")
    كما يمكن تجزئة النص بنفس الطريقة التي تعلمناها عن القوائم كما يلي:
    text="Hello python" text[0]
    text[-1]
    text[6:8]
    ويمكن دمج البيانات النصية باستخدام علامة الجمع كما في المثال التالي:
    name="Ahmad" txt="Hello" space=" " txt+space+name
    وكذلك يمكن استخدام الدالة ()join للقيام بنفس المهمة كما في المثال التالي:
    name="Ahmad" txt="Hello" " ".join([txt,name])
    لاحظ اننا في المثال السابق استخدمنا الدالة ()join مسبوقة بفراغ موجود بين علامتي التنصيص ليكون رابطاً لعناصر القائمة الموجود داخل قوسي الدالة. كما يجب ملاحظة انه يمكن استخدام أي رابط اخر نرغب فيه فمثلا لو أردنا استخدام الشرطة السفلية فإننا نكتب المثال السابق كالتالي:
    "_".join([txt,name])
    ".".join([txt,name])
    ويمكن استخدام الدالة ()count كما فلعنا مع القوائم لمعرفة عدد تكرار حرف او جزء من نص في بيان نصي كما في المثال التالي:
    text="Hello python" text.count("l")
    text.count("p")
    text.count("P")
    وكذلك يمكن استخدام الدالة ()index لمعرفة مؤشر أول ظهور لحرف او جزء من نص داخل بيان نصي كما يلي:
    text="Hello python" text.index("H")
    text.index("n")
    text.index("py")

    تمارين استكشافية

    1- اكتب متغيرا وليكن name واجعل قيمته أي اسم تختاره وليكن “Ali” حاول ان تطبع العبارة “Hello name” باستخدام الدالة ()print؟ ماذا تلاحظ؟ هل قام بايثون باستخدام قيمة name عند اجراء عملية الطباعة؟ اذا كانت الإجابة بلا فما السبب في رأيك؟
    ان وجود اسم متغير داخل علامة تنصيص لا يعتبره بايثون على انه متغير وانما يتعامل معه على انه نص كأي نص اخر. ولكي نجعل بايثون يستخدم قيمة متغير دخال علامة تنصيص فان هناك عدة حيل في بايثون تمكنا من القيام بذلك. اول هذه الحيل هي استخدام النسبة المئوية دخال النص متبوعة بنوع البيان المراد إدخاله وهو أسلوب قديم تم استيراده من لغة C كما في المثال التالي:
    name="Hassan" greating = "Hello, %s" % name print(greating)
    لاحظ ان علامة النسبة المئوية استخدمت لتحديد مكان ادخال قيمة المتغير وحرف s استخدم للدلالة على نوع القيمة التي يحتويها المتغير والذي تم وضعه بعد النص مفصول عنه بعلامة النسبة المئوية. كما يمكن استخدام هذه الطريقة لإدخال قيم متغيرات رقمية كما في المثال التالي:
    speed = 120 print("You exceeded the speed limit of %d km/hour" % speed)
    print("You exceeded the speed limit of %f km/hour" % speed)
    print("You exceeded the speed limit of %5.2f km/hour" % speed)
    لاحظ في المثال السابق ان الحرف d يستخدم للدلالة على الاعداد الصحيحة بينما الحرف f يدل على استخدام الاعداد العشرية. كما يجب ملاحظة ان عدد الخانات الرقمية يمكن التحكم فيه بتحديد عدد الخانات الكلي وعدد الأجزاء العشرية كما موضح في المثال الأخير.
    ويمكن ادخال قيم أكثر من متغير سواء كانت عدديا أو نصيا كما في المثال التالي:
    name="Ahmad" speed=120 fine=300 print("Dear %s, You violated the speed limit of %d. You have to pay %5.2f Riyals" % (name,speed,fine))
    لاحظ استخدامنا للصف في المثال السابق بعد علامة النسبة المئوية ولاحظ اهمية الترتيب للبيانات داخل الصف لتوظيفها بشكل مناسب داخل النص.
    وهناك طريقة حديثة للقيام بنفس المهام السابقة وذلك باستخدام الدالة ()format كما في المثال التالي:
    name="Ahmad" speed=120 fine=300 print("Dear {}, You violated the speed limit of {} km/hr. You have to pay {} Riyals".format(name,speed,fine))
    وترتيب المتغيرات بين قوسي الدالة ()format يجب مراعاته أيضا هنا الا اذا كتبنا اسم المتغيرات بين القوسين المتعرجين او استخدمنا مؤشر ترتيب المتغير كما في المثالين التاليين:
    print("Dear {2}, You violated the speed limit of {0} km/hr. You have to pay {1} Riyals".format(speed,fine,name))
    print("Dear {name}, You violated the speed limit of {speed} km/hr.\ You have to pay {fine} Riyals".format(speed=speed,fine=fine,name=name))
    وفي اصدارة بايثون 3.6 وما بعدها تم إضافة طريقة أخرى لتهيئة النصوص تسمى literal formatting وهي تشبه الى حد كبير استخدام الدالة ()format كما هو موضح في المثال التالي:
    print(f"Dear {name}, You violated the speed limit of {speed} km/hr. You have to pay {fine} Riyals")
    فكل ما تحتاجه عند استخدام هذه الطريقة هو إضافة حرف f قبل النص وكتابة أسماء المتغيرات التي تريد التعويض بقيمها داخل قوسين متعرجين فقظ.
    لتجزئة نص الي بيانات نصية صغيرة داخل قائمة يمكن استخدام الدالة ()split كما في المثال التالي:
    text="one two three four five six seven" text.split(" ")
    colors="yellow,green,blue,red,white,black" colors.split(",")
    لاحظ اننا ستخدمنا في المثال السابق الفراغ والفاصلة لتخبر مفسر بايثون بأماكن فصل النص ووضعنا هاتين العلامتين داخل علامة تنصيص بين قوسي الدالة ()split.
    ويمكن تحويل النص الى حروف كبير او صغيرة باستخدام الدالتين ()upper و ()lower على التوالي كما في المثال التالي:
    colors="yellow,green,blue,red,white,black" colors.upper()
    numbers="ONE TWO THREE FOUR" numbers.lower()
    كما يمكن إزالة الفراغات الموجود في بداية النص ونهايته باستخدام الدالة ()strip كما في المثال التالي:
    text=" Hello python " text.strip()
    كما يمكننا أيضا استبدال حرف او جزء من نص باخر باستخدام الدالة ()replace كما في المثال التالي:
    text="Hello python" text.replace('l','s')
    ونختم هذا الفصل بان نخبرك ان البيانات النصية تختلف عن القوائم في انها غير قابلة للتغيير immutable. بمعنى ان ما قمنا به في الأمثلة السابقة من تعديلات على النص لم يكمن بالفعل على نفس النص وانما قام مفسر بايثون بإنشاء بيان نصي جديد يحتوي على التعديلات التي قمنا بها وقام بطباعتها على الشاشة بينما البيان الأصلي لم يتغير وسف نثبت لك ذلك في المثال التالي:
    numbers="one two three four" numbers.upper()
    numbers
    لاحظ انه عندما قمنا بتحويل النص numbers الى حروف كبيرة قام بايثون بانشاء متغير مؤقت وطبع النص بعد التعديل على الشاشة. ولكن عندما طلبنا من بايثون طباعة النص الأصلي فان النص مازال كما هو بحروفه الصغيرة. هذا الخاصية لا تنطبق على القوائم. فعندما نقوم بالتعديل على القوائم فان بايثون يجري عملية التعديل على القائمة الاصلية كما في المثال التالي:
    numbers=["one","two","three","four"] numbers.pop() numbers
    لاحظ انه عندما حذفنا البيان الأخير من القائمة numbers باستخدام الدالة ()pop قام بايثون بحذف البيان من القائمة الاصلية.

    ملخص الفصل

    اسئلة استيعابية


    الفصل الرابع : اتخاذ القرارات


    ان من الاشياء الجميلة في لغة البرمجة ان المبرمج يستطيع ان يعطي الكمبيوتر قدرة على التفكير "ان صح التعبير" والتي من خلالها يستطيع الكمبيوتراتخاذ قراراته. لكن في حقيقة الامر هذه القرارات ماهي الا شروط يفرضها المبرمج على البرنامج ليحقق مبتغاه. في هذا الفصل سوف نتحدث عن كيفية اتخاذ القرارات في بايثون باستخدام اداة الشرط if.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. التعرف على كيفية استخدام أداة الشرط if
    2. التعرف على كيفية استخدام else و elif مع أداة الشرط if.
    3. التعرف على طريقة كتابة أداة شرط داخل أداة شرط أخرى.

    التركيب اللغوي

    التركيب اللغوي في بايثون هنا مشابه لغيره من لغات البرمجة. فهو يهدف في المقام الاول الى التحكم في سير تنيفذ البرنامج بوضع شروط يفرضها المبرمج حين كتابته للكود البرمجي على البرنامج بحيث تكون نتائح هذه الشروط احدى قيمتين صح او خطأ. في حال الحصول على القيمة صح من عملية التحقق من الشرط يقوم مفسر بايثون بتنفيذ اوامر يحددها له المبرمج اما في حال الحصول على القيمة خطأ فان بايثون يتخطأ الاوامر التي يتطلب تنفيذها تحقق الشرط كما هو موضح في الشكل ادناه.

    يبتدتئ التركيب اللغوي لأتخاذ القرارات بكلمة if متبوعا بالشرط المراد التأكد منه ومن ثم وضع نقطتين فوق بعض في نهاية السطر كما في المثال التالي:
    name="Ahmad" if len(name)<10:print(name)
    ففي المثال السابق يتم التحقق من صحة كون عدد الاحرف الموجودة في كلمة “Ahmad” اصغر من العدد 10 . فاذا كانت العبارة صحيحة فان مفسر بايثون يقوم بتنفيذ العبارة التي تتلو النقطتين فقوق بعض. اما إذا كانت العبارة خاطئة فان مفسر بايثون يتجاهل تنفيذ العبارة التي تتلو النقطتين فوق بعض. وبما ان هذه العبارة صحيحة فانه يتم تنفيذ الكود (print(name الذي يتلو النقطتين فوق بعض. ان كتابة العبارة المراد تنفيذها بعد تحقق الشرط يفضل ان تكون في سطر مستقل بدلا من ان تكون في نفس السطر ولكن إذا جعلت في سطر مستقل فانه يتوجب ترك مسافة بمقدار حرف واحد على الأقل في بداية السطر الجديد (بفضل ترك مسافة بمقدار أربعة أحرف كما هو متعارف عليه بين مبرمجي بايثون) لكي يتمكن مفسر بايثون من التفرقة بين العبارة المشروطة والعبارة الاعتيادية التي قد تقع بعدها كما في المثال التالي:
    name="Ahmad" if len(name)<10: print(name) #عبارة يتطلب تنفيذها تحقق شرط الجملة الشرطية x=5 #عبارة غير داخلة في الجملة الشرطية print(x) #عبارة غير داخلة في الجملة الشرطية
    لاحظ في المثال السابقة ان ترك مسافة في العبارة المشروطة كان ضروري لتمكين مفسر بايثون من التفرقة بين العبارة الشرطية وما يليها من عبارات أخرى غير مرتبطة بالجملة الشرطية. لذلك كل العبارات التي يتطلب تنفيذها تحقق الشرط يجب ان تترك مسافة في بداية السطر وتكون هذه المسافة هي نفسها لجميع العبارات التي تتطلب تحقق الشرط. المثال التالي يوضح تنفيذ أكثر من عبارة تتطلب تحقق شرط معين:
    name="Ahmad" if len(name)<10: print(name) #عبارة يتطلب تنفيذها تحقق شرط الجملة الشرطية لاحظ تساوي المسافات المتروكة print(len(name)) #عبارة يتطلب تنفيذها تحقق شرط الجملة الشرطية لاحظ تساوي المسافات المتروكة y=0 #عبارة يتطلب تنفيذها تحقق شرط الجملة الشرطية لاحظ تساوي المسافات المتروكة x=7 #عبارة غير داخلة في الجملة الشرطية لاحظ بداية كتابتها من اول السطر print(x) #عبارة غير داخلة في الجملة الشرطية لاحظ بداية كتابتها من اول السطر

    تمارين استكشافية

    ١-قم بكتابة المثال السابق واجعل واحدة من العبارات المشروطة تبتدأ بعدد مختلف من الفراغات عن العبارتين الأخريين ماذا تلاحظ؟
    ٢- قم بكتابة جملة شرطية للتعرف على ما اذا كان العدد 6 موجود في القائمة [1,2,3,4] بحيث يقوم بطباعة هذا الرقم اذا كان الرقم موجود فعلا؟ ماذا تلاحظ؟

    لنرفض اننا نريد ان نقوم بعمل ما إذا تحقق شرط معين ونقوم بعمل اخر إذا لم يتحقق الشرط. للقيام بهذه المهمة نحتاج الى استخدام else في الجملة الشرطية لتخبر بايثون بالمراد فعله في حين عدم تحقق الشرط المطلوب كما هو موضح في المثال التالي:
    name="Ahmad" if len(name)<4: print(name) print(len(name)) y=0 else: print("the name is greater than 4 letters")
    لاحظ ان else تخضع لجميع شروط الجملة الشرطية السابقة من حيث انها تنتهي بنقطتين ويتوجب ترك مسافة في بداية العبارات التي يتطلب تنفيذها عدم تحقق الشرط.

    تمارين استكشافية

    قم بكتابة جملة شرطية للتعرف على ما اذا كان العدد 6 موجود في القائمة [1,2,3,4] بحيث يقوم بطباعة هذا الرقم اذا كان الرقم موجود فعلا؟ واذا لم يكن موجودا اطبع عبارة تفيد ان الرقم غير موجود؟
    لنفرض اننا نريد ان نتحقق من توفر شرطين قبل ان نقوم بعمل ما ونقوم بعمل آخر في حال عدم توفر الشرطين. ماذا بإمكاننا ان نصنع في هذه الحالة؟ هناك حلول كثيرة فمنها مثلا ان نستخدم الجملة الشرطية if…else للتحقق من الشرط الاول والثاني باستخدام أداة الربط المنطقي and وفي حال عدم تحقق الشرطين يتم تنفيذ العبارة التي تلي else كما في المثال التالي:
    name="Ahmad" if len(name)<10 and name=="Ahmad": print(name) else: print("the name is wrong or big")
    كما يمكن القيام بنفس المهمة باستخدام الجملة الشرطية البسيطة if للتحقق من الشرط الاول ومن ثم التحقق من الشرطة الثاني باستخدام الجملة الشرطية if … else كما في المثال التالي:
    name="Ahmad" if len(name)<10: if name=="Ahmad": print(name) else: print("the name is wrong or big")
    في المثال الاخير هذا النوع من الاستخدام للأدوات الشرطية يسمى الأدوات الشرطية الفرعية. بحيث يتم استخدام أداة شرطية داخل أداة شرطية أخرى. ويمكن استخدام التركيب elif للتحقق من شرط اخر في حال عدم تحقق شرط سابق كما في المثال التالي:
    name="Ahmad" if len(name)>10: print(name) elif name=="Ahmad": print(name) else: print("the name is wrong or big")
    يجب الاشارة الى ان بايثون يعتبر القيمة صفر او القيمة النصية الفارغة او قيمة None كقيمة خاطئة في حال استخدامها مع if كما في المثال التالي:
    balance=0 if balance: print(balance) else: print("No money in your balance")
    name="" if name: print(name) else: print("please enter valid name")
    value=None if value: print(value) else: print("the value is None")
    كما ان بايثون يعتبر اي قيمة عددية غير الصفر او اي بيان نصي يحتوي على حرف او اكثر قيمة صحيحة عند التعامل بها مع اداة الشرط كما في الامثلة التالية:
    balance=253 if balance: print(balance) else: print("No money in your balance")
    name="Ayman" if name: print(name) else: print("please enter valid name")
    value=(1,2,3,4) if value: print(value) else: print("the value is None")

    الفصل الخامس : حلقات التكرار


    ان من اكثر بواعث الملل ان يضطر الانسان الى اداء الاعمال ذاتها مرارا وتكرارا. وذلك لانه يرى في هذه الاعمال هدرا لوقته وجهده. فنجده قد بدأ بتحرير نفسه من قيود هذه الاعمال باسنادها الى الات من صنعة. ومن اشهر الالات التى اعتمد عليها لاداء هذا الغرض جهاز الكمبيوتر. ونجد ان حلقات التكرار المسوؤلة عن اعداد المهام المتكررة قد اصبحت جزء رئيسي وجوهري من اجزاء معظم لغات البرمجة المشهور. وفي لغة بايثون يتم عمل المهام المتكررة بواسطة تركيبين لغويين اساسين هما حلقة for وحلقة while واللتان تعتبران المحواران الرئيسيان لهذه الفصل.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. التعرف على كيفية استخدام حلقة التكرار for.
    2. التعرف على كيفية استخدام حلقة التكرار while.
    3. التعرف على مواضع التشابه والاختلاف بين مهام كل حلقة.

    أولا: حلقة for

    تستخدم حلقة for التكرارية عندما يراد اداء مهمة ما عددأ معلوما من المرات. وافضل طريقة لتفصيل التركيب اللغوي لهذه الحلقة هي اعطاء امثله توضح طريقة عملها فالمثال التالي يقوم بطباعة احرف كلمة بايثون واحدا تلو الاخر:
    for x in "python": # عدد مرات التكرار يحدد هنا بعدد احرف كلمة بايثون print(x) # المهمة التي يراد تكرارها تكتب هنا # لاحظ الفراغ الذي ترك في بداية السطر
    من المثال السابق يمكن ملاحظة الاتي :
    1. تبدأ الحلقة بكلمة for.
    2. ينتهي السطر الاول من الحلقة بنقطتين فوق بعض ":". ويعتبر نسيان كتابة النقطتين من اشهر الاخطاء التي يقع فيها المبرمجين المبتدئين في لغة بايثون. لذلك احرص على تذكرها جيدا.
    3. لاعلام بايثون بالمهمة التي يراد تكرارها يترك فراغ في بداية السطر بمقدار حرف واحد على الاقل (Indentation). ومن المتعارف عليه في لغة بايثون ان يترك فراغ بمقدار 4 احرف.
    4. يمكن كتابة المعنى الحرفي للتركيب اللغوي السابق كالاتي: "لكل حرف يرمز اليها بالمتغير x موجودة في البيان النصي "python" قم بطباعة قيمة x على الشاشة.
    5. اسم المتغير هنا اختياري فيمكننا ان نستبدله باي متغير نحب فيمكننا مثلا ان نسميه letter فنحصل على نفس النتيجة كما في المثال التالي:
    for letter in "python": # عدد مرات التكرار يحدد هنا print(letter) # المهمة التي يراد تكرارها تكتب هنا # لاحظ الفراغ الذي ترك في بداية السطر
    اذا اردنا ان نكرر اكثر من مهمة فانه يجب مراعاة ان يكون عدد الفراغات المتروكة في بداية سطر كل مهمة متساويا كما في المثال التالي:
    for i in "car": print(i) print(i)
    عند اختلاف عدد المسافات المتروكة في بداية سطر كل مهمة فان بايثون يعطي ملاحظة على وجود خطأ كما في المثال التالي:
    for letter in "car": print(i) print(i)
    في المثال السابق استخدمنا عدد الاحرف الموجودة في بيان نصي لتحديد عدد مرات التكرار. وهذه الطريقة ليست الوحيدة لتحديد عدد مرات التكرار فهناك البيانات التجميعية التي ذكرناها في الفصل الثالث يمكن ان نستخدمها ايضا لتحديد عدد مرات التكرار. فالبيانات الموجودة في قائمة يمكن ان تستخدم لتحديد عدد مرات التكرار كما في المثال التالي:
    students=['Ali','Ahmad','Hind','Omar'] for name in students: print(name)
    لاحظ اننا استخدمنا اسم المتغير هنا ليكون name وكان بامكاننا ان نستخدم اي اسم اخر فالعملية هنا اختيارية بحته.
    وكذلك الصفوف يمكن ان تستخدام لاجراء عمليات التكرار المحدد كما في المثال التالي:
    for number in (1,2,3): #استخدام الصفوف لإجراء عملية التكرار على عناصرها print(number)
    تعلمنا في الفصل الثالث ايضا ان الدالتان ()keys و ()value عند استخدامها مع القواميس تعطي قائمة بالمفاتيح والقيم لذلك يمكن اجراء علميات التكرار عليها كما يلي:
    d={1:"a",2:"b",3:"c"} print(d.values()) print(d.keys())
    for value in d.values(): print(value)
    for key in d.keys(): print(key)
    كما يمكن استخدام الدالة ()range لتحديد عدد مرات التكرار وذلك لانها تقوم بانشاء قائمة من اعداد صحيحة كما في المثال التالي:
    for number in range(5): print(number)
    فالدالة ()range في الاصل تاخد ثلاث قيم من الاعداد الصحيحة اثنان منها اختياريان يمكن حذفهما كما في المثال السابق و الثالث متطلب اساسي لانه يحدد الرقم الذي يجب ان تقف عنده قيم القائمة. فالعددان الاختياريان هما العددان الذان يحددان بداية القيمة التى تبدأ عندها القائمة والاخر يحدد عدد الخطوات التى تكون بين قيم القائمة.


    فعند حذف القيم الاختيارية يفترض بايثون انك تريد قائمة من اعداد صحيحة تبدأ من الصفر وتزيد بمقدار العدد 1. فالمثال السابق قام بايثون بانتاج قائمة من الاعداد الصحيحة بدأت من الصفر وتزايدت بمقدار 1 وتوقفت قبل الرقم 5. لاحظ ان العدد الذي يحدد توقف القائمة لايكون مشمولا في القائمة. ويمكن استخدام الدالة ()range بثلاث قيم كما في المثال التالي:
    for number in range(4,10,2): print(number)
    فالقائمة السابقة بدأت بالعدد 4 و انتهت قبل العدد 10 وكان عدد الخطوات بمقدار 2.
    كما يمكن ان يكون عدد الخطوات ذو قيمة سالبة لذلك يجب ان تكون قيمة البداية اكبر من النهاية كما في المثال التالي:
    for number in range(16,6,-3): print(number)
    عند استخدام قيمتين في الدالة ()range فان بايثون يفترض ان القيمة التي حذفت هي قيمة عدد الخطوات كما في المثال التالي:
    for x in range(2,5): print(x)

    تمارين استكشافية

    ١- اكتب برانامج يقوم بطباعة الأرقام من 0 الي 10 ؟
    ٢- اكتب برنامج يقوم بطباعة الاعداد الزوجية من 50 الى 100 ؟
    ٣- اكتب برنامج يقوم بجمع الاعداد من 1 الى 100 ؟
    ٤- اكتب برنامج يقوم بطباعة كلمة “Hello” معكوسة؟
    ٥- اكتب برنامج يقوم بطباعة الاعداد تنازلياً من 20 الي 0 ؟

    ثانيا: حلقة while

    الحلقة التكرارية while تستخدم عندما يكون عدد مرات التكرار التي يريد المبرمج القيام بها غير معروفه مسبقا. لذلك تستخدم هذه الحلقة التكرارية عندما نريد ان نقوم بعمليات تكرار غير منتهية او عندما لا يتم معرفة إمكانية الخروج من الحلقة التكرارية الا بعد اجراء بعض العمليات داخل الحلقة التكرارية. ويكون التركيب اللغوي لهذه الحلقة مشابها لحد ما لحلقة for كما في المثال التالي:
    a=1 while a<10: print(a) a=a+2
    من المثال السابق يتضح الاتي:
    1. تبدأ حلقة التكرار بكلمة while.
    2. استمرارية حلقة التكرار يكون محكوماً بالشرط الذي يتلو كلمة while.
    3. ينتهي السطر الاول من الحلقة بنقطتين فوق بعض ":".
    4. يترك مسافة بمقدار حرف واحد على الأقل (Indentation) قبل كتابة الاوامر التي يراد تكرارها. ومن المتعارف عليه في لغة بايثون ان يترك فراغ بمقدار 4 احرف.
    يمكن كتابة المعنى الحرفي للمثال السابق على النحو التالي: "طالما قيمة a اقل من 10 فقم بتكرار عملية طباعة a."
    وبما ان قيمة a تزيد داخل حلقة التكرار بمقدار 2 في كل مره فان طباعة a سوف تتكرر بحسب شرط الحلقة التكرارية وهو ان قيمة a لابد ان تكون اقل من 10. تمعن في الشكل التالي لفهم ما يقوم به مفسر بايثون داخل حلقة while.


    الفصل السادس : كتابة الدوال


    في هذا الفصل سوف نتطرق الى معرفة الدوال والتي هي عبارة عن أكود برمجية مستقلة يمكن استدعاءها متى واينما شاء المبرمج. الغرض الاساسي من استخدامها يكمن في تجزئة البرنامج الى اجزاء صغيرة تساعد المبرمج على عدم تكرار الأكواد داخل البرنامج وتسهل عليه عمليه اصلاح الاخطاء.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. كيفية كتابة دالة وطريقة استدعائها.
    2. التعرف على أنواع مدخلات الدوال وكيفية استخدامها.
    3. التعرف على مخرجات الدوال.
    4. التعرف على طريقة الصحيحة لكتابة تعليقات ارشادية بكيفية استخدام الدوال.

    تعريف الدوال

    لكتابة دالة في بايثون نستخدم def في بداية السطر لتبليغ مفسر بايثون باننا نرغب في تعريف دالة جديدة ثم نتبعها باسم الدالة الذي ينتهي بقوسين يوضع بداخلهما مدخلات الدالة وفي نهاية السطر نكتب نقطتين فوق بعض كما في المثال التالي:
    def function_name(): pass
    لاحظ ان في الدالة السابقة كتبنا اسم الدالة ليكون function_name وبإمكانك ان تستخدم أي اسم آخر شريطة ان تنطبق عليه شروط كتابة اسم المتغيرات التي تعرفنا عليها في الفصل الثاني. كما اننا في المثال السابق لم نقم بوضع أي مدخلات داخل قوسي الدالة لأن ذلك الامر اختياري بحسب احتياج الدالة للمدخلات. وأخيراً فان كلمة pass وهي تعنى ان الدالة تم تعريفها فقط و لا تقوم بعمل اي شيء حتى الان. ولكي نجعل الدالة تقوم بعمل ما فإننا نقوم بكتابة الكود البرمجي ليحل محل كلمة pass كما في المثال التالي:
    def hello(): print("hello I am inside a function")
    لاحظ ايضا ان الكود البرمجي داخل الدالة يكتب بعد ترك مسافة على الاقل بقيمة حرف والمتعارف عليه في بايثون يتم ترك فراغ بمقدار اربعة احرف.جميع اسطر الكود البرمجي يجب ان تترك نفس الفراغ والا سوف يعطي مفسر بايثون اشعارا بوجود خطأ.
    لا يتم تنفيذ الكود داخل الدالة الا بعد استدعائها وذلك بكتابة اسم الدالة متبوعا بقوسين بداخلهما مدخلات الدالة ان كان هناك مدخلات اما اذا لم يكن هناك مدخلات فيكتفى بكتابة قوسين فارغين كما في المثال التالي:
    hello()
    لذلك يمكن كتابة تعريف الدالة في اي مكان من البرنامج بشرط ان لا يسبق استدعاءها تعريفها عند كتابة البرنامج. لاحظ ايضا ان في الامثلة السابقة كتبنا قوسي الدالة فارغين لان الدالة لاتتطلب اي معلومات اضافية لتنفيذ الكود البرمجي داخلها. لنفرض الآن اننا نريد كتابة دالة اكثر ذكاء بحيث تقوم بالقاء التحية على اي شخص ندخل اسمه للداله. للقيام بهذه المهمة يمكن كتابة الدالة بالطريقة التالية:
    def hello(name): print("Hello "+name)
    لاحظ عند كتابتنا للدالة السابقة قمنا باعطاء اسم الشخص المراد القاء التحية عليه متغير اسميناه name ووضعناه داخل قوسي الدالة. ومن ثم استخدمنا هذا المتغير في كتابة امر الطباعة. يسمى هذه المتغير مدخل الدالة. وعند استدعاء الدالة ما علينا القيام به هو ادخال اسم المراد القاء التحية عليه كنص كما في المثال التالي:
    hello("Ahmad") hello("Ali") hello("Sara")
    كما يمكن كتابة دالة باكثر من مدخل يفصل بينها بفاصلة داخل قوسي الدالة كما في المثال التالي:
    def addition(x,y): print("مجموع الرقمين هو:", x+y) addition(3,4)
    يجب الاشارة ان مدخلات الدالة تنقسم الى قسمين. القسم الاول يسمى مدخلات الزامية بحيث انه متى تم كتابتها داخل قوسي الدالة عند تعريفها فان الدالة لايمكن استدعاءها الا بعد تزويدها بهذه المدخلات. والقسم الثاني يسمى مدخلات اختيارية. يحيث تعطى هذه المدخلات قيم افتراضية عند تعريف الدالة بستخدام علامة اليساوي "=". وعند استدعاء الدالة يمكن للمبرمج استبدال القيم الافتراضية بقيم جديدة والا قام مفسر بايثون باستخدام القيم الافتراضية اذا تم تجاهلها من قبل المبرمج وقت الاستدعاء.ولكي نوضح الفرق بين هذين االنوعين للننظر للمثال التالي:
    def addition(x,y=4): print(x+y)
    يعتبر المدخل x الزامي بينما المدخل y اختياري وذلك لاننا قمنا باعطائة قيمة افتراضية هي العدد 4.
    الان اذا اردنا ان نستدعى الدالة السابقة بدون مدخلات فان مفسر بايثون سوف يعطينا اشعارا بوجود خطأ كما في المثال التالي:
    addition()
    لنقوم الآن بااستخدام مدخل واحد لاستدعاء الدالة كما في المثال التالي:
    addition(2)
    المدخل الذي استخدمناه في المثال السابق هو المدخل الالزامي والذي يمثل قيمة x اما المدخل الاختياري فقد قام مفسر بايثون باستخدام قيمته الافتراضية (4) عند اهملنا ادخاله وقت استدعائنا للدالة. اذا رغبنا في تغيير القيمة الافتراضية للمدخل y فاننا نقوم بادخال القيمة وقت استدعاء الدالة بعد ادخال قيمة x اولا كما في المثال التالي:
    addition(2,9)
    ان ترتيب ادخال قيم المدخلات عند استدعاء الدالة مهم جدا ففي المثال السابقة القيمة x اخذت القيمة 2 والقيمة y اخذت القيمة 9 وذلك بحسب الترتيب المتبع عند تعريف الدالة. يجب الاشارة الى انه اذا تم استخدام اسم المدخل عند استداعاء الدالة فان ترتيب المدخلات يصبح غير مهم كما في المثال التالي:
    addition(y=8,x=1)
    ففي المثال السابق تم ادخال قيمة y قبل قيمة x ولكن مفسر بايثون تعرف على قيم كل مدخل عندما زودناه باسماء المدخلات لذلك اصبح ترتيب المدخلات غير مهم. يجب ان نشير الى ان مفسر بايثون لايسمح بتعريف دالة تحتوي على مدخلات الزامية ومدخلات اختيارية الا بترتيب معين. وهو البدء بالمتغيرات الالزامية اولا ثم المدخلات الاختيارية. وعند تعريف دالة بعكس ذلك فان مفسر بايثون يعطي اشعارا بوجود خطأ كما في المثال التالي:
    def addition(y=4,x): print(x+y)
    لنفرض الان اننا لا نريد طباعة ما تقوم به الدالة على الشاشة ولكن فقط نريد ارجاع ناتج الدالة بيحث نستطيع كتابة امر الطباعة متى ما شئنا. لكي نقوم بهده المهمة لابد لنا من استخدام الامر return لاخبار الدالة بارجاع محصلة الكود البرمجي داخل الدالة كما في المثال التالي:
    def addition(x,y): return x+y sum=addition(3,7)
    في المثال السابق تم اسناد ناتج الدالة الى المتغير sum. ولكي نتأكد ان العملية تمت بنجاح لنقوم بطباعة قيمة sum كما في المثال التالي:
    print(sum)
    في حقيقة الامر ان الدالة دائما تقوم بارجاع محصلة حتى لو لم نكتب ما نريد ارجاعه بعد الامر return. فاذا لم نستخدم الامر return فان الدالة تقوم بارجاع قيمة افتراضية هي None وتعني انه لم يتم ارجاع اي شئ. كما في المثال التالي:
    def addition(x,y): z=x+y sum=addition(5,9) print(sum)
    لاحظ ان الدالة السابقة قامت بجمع المدخلين واسناد النتيجة الى المتغير Z. وبما اننا لم نستخدم الامر return لارجاع قيمة المتغير z فان الدالة قامت بارجاع القيمة الافتراضية None. هذي النقطة تقودنا الى ما يسمى بمدى المتغيرات داخل الدوال. فالمتغير z في الدالة السابقة سوف يمسح من الذاكرة بمجرد الانتهاء من استدعاء الدالة لذلك تسمى هذه المتغيرات بالمتغيرات المحلية (local variables). اما المتغيرات التى تقع خارج الدوال فتسمى متغيرات عالمية (global variables) بحيث تحتفظ بقيمتها في اي مكان من البرنامج حتى في داخل الدوال كما في المثال التالي:
    x=5 def addition(y): print(x+y) addition(4)
    لكن هناك امر مهم يجب ان تعرفه وهو اننا لايمكننا من تغيير قيم المتغيرات العالمية داخل الدوال الا بعد استخدام كلمة global قبل اسم المتغير المراد تغيير قيمته كما في المثال التالي:
    x=5 def change_x(y): global x x=y change_x(3) print(x)
    لاحظ ان كلمة global في المثال السابق ابلغت مفسر بايثون ان هذا المتغير عالمي ويجب الاحتفاظ بقيمته حتى بعد الانتهاء من مهمة الدالة. اما اذا لم نستخدم كلمة global فان مفسر بايثون سوف يفترض ان x متغير محلى وسوف يمحو قيمته من الذاكرة بعد الانتهاء منها ولا يحدث اي تغيير للمتغير العالمي كما في المثال التالي:
    x=5 def change_x(y): x=y change_x(4) print(x)
    ينص دليل بايثون الارشادي رقم PEP 8 الخاص بتوحيد طريقة تنسيق وكتابة الاكواد البرمجية وجعلها سهلة القراءة لكافة المبرمجين على انه يجب كتابة ملخص على شكل تعليق او ملاحظة في السطر الذي يتلو اسم الدالة بحيث يشير فيه المبرمج على الوظيفة التي تقوم بها الدالة كما في المثال التالي:
    def add_two_numbers(x,y): '''تقوم هذه الدالة بحساب مجموع الرقمين المدخلين وارجاع حاصل جمعها''' return x+y
    يمكن التعرف على ملخص وظيفة الدالة اذا تم كتابته عند تعريف الدالة باستخدام الامر "\__doc__" بعد كتابة اسم الدالة متبوعا بنقطة كما في المثال التالي:
    print(add_two_numbers.__doc__)

    التعليقات الارشادية للدوال

    ان اول خطوة يقوم بها المبرمج لتسهيل فهم الدالة التي يقوم بإنشائها هي اختيار اسم مناسب لها بحيث يكون هذا الاسم عنوانا لما تقوم به الدالة. لكن في بعض الأحيان يتطلب الامر كتابة اسما طويلا للدالة فيصعب بذلك عملية استدعائها لذلك قد يكتفى بكتابة اسم مختصر للدالة ومن ثم يتم كتابة تعليق بداخلها لشرح طريقة عملها وما تقوم به من مهام. فنجد ان دليل بايثون الارشادي رقم PEP 8 الخاص بتوحيد طريقة تنسيق وكتابة الأكواد البرمجية وجعلها سهلة القراءة لكافة المبرمجين ينص على انه يجب كتابة ملخص على شكل تعليق او ملاحظة في السطر الذي يتلو اسم الدالة بحيث يشير فيه المبرمج على الوظيفة التي تقوم بها الدالة كما في المثال التالي:
    def add_two_numbers(x,y): '''تقوم هذه الدالة بحساب مجموع الرقمين المدخلين وارجاع حاصل جمعها''' return x+y
    يمكن التعرف على ملخص وظيفة الدالة إذا تم كتابته عند تعريف الدالة باستخدام الامر "__doc__" بعد كتابة اسم الدالة متبوعا بنقطة كما في المثال التالي:
    def add_two_numbers(x,y): '''تقوم هذه الدالة بحساب مجموع الرقمين المدخلين وارجاع حاصل جمعها''' return x+y print(add_two_numbers.__doc__)

    تمارين استكشافية

    1. تعرف على التعليمات الارشادية للدالة ()len التي تعلمناها سابقاً؟
    1. تعرف على التعليمات الارشادية للدالة ()type ؟
    1. تعرف على التعليمات الارشادية للدالة ()print ؟

    الفصل السابع : التعامل مع الملفات


    ان جميع البرامج التي قمنا بكتابتها حتى الان كانت تتطلب ادخال البيانات يدويا داخل الكود البرمجي قبل تشغيله. وهذه الطريقة تعتبر بدائية الى حد ما وغير مفيدة خاصة عندما نرغب في ادخال بيانات جديدة غير التي ادخلناها قبل تشغيل البرنامج. لذلك سوف نتطرق الى معرفة كيفية ادخال البيانات للبرنامج بشكل تفاعلي باستخدام الدالة ()input وكذلك طريقة التعامل مع الملفات من اجل قراءتها او من اجل التعديل عليها وحفظ التعديلات التي اجريت عليها لاستخدامها لاحقا بعد اغلاق البرنامج.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. معرفة كيفية استخدام الدالة ()input من اجل ادخال البيانات للبرنامج بشكل تفاعلي.
    2. التعرف على طريقة فتح الملفات النصية.
    3. التعرف على طريقة قراءة البيانات النصية.
    4. التعرف على طرق الكتابة والحفظ على ملف نصي.

    ادخال البيانات بشكل تفاعلي

    في لغة بايثون يتم تكمين مستخدمي البرنامج من ادخال بياناتهم بعد تشغيل البرنامج بغية معالجتها وتحليلها من خلال استخدام الدالة ()input كما في المثال التالي:
    name=input("please enter your name: ") print("Hello ",name)
    فالتركيب اللغوي السابق يطلب من المستخدم بعد تشغيله للبرنامج ان يقوم بإدخال قيمة المتغير name وذلك من خلال كتابة ملاحظة او رسالة يكتبها المبرمج بين قوسي الدالة ()input على شكل نص لترشد المستخدم الى ما يجب عمله. ويبقى البرنامج منتظراً للمستخدم حتى يدخل قيمة ويضغط على زر الادخال ليكمل بعدها البرنامج تنفيذ ما تبقى من كود برمجي.
    يجب ملاحظة ان كل ما يقوم بإدخاله المستخدم من بيانات في هذا التركيب اللغوي يتم التعامل معه كسلسلة نصية. ويمكن التأكد من هذه المعلومة باستخدام الدالة ()type كما في المثال التالي:
    num=input("Please enter a number: ") print(type(num))
    لاحظ انه بعد ادخال الرقم 10 والضغط على زر الادخال طبع لنا البرنامج نوع البيان الذي ادخلناه وهو من النوع النصي “str”. وكمثال آخر لنفرض اننا نريد كتابة برنامج يطلب من المستخدم ادخال قيمة رقمين x وy لكي يقوم بحساب حاصل جمعهما. فمن خلال تعلمنا للتركيب اللغوي السابق يمكن كتابة البرنامج
    x=input("please enter a value for x: ") y=input("please enter a value for y: ") sum=x+y print("The sum of x and y = ",sum)
    عند تنفيذ البرنامج السابق وإدخال قيمة 5 للمتغير x وقيمة 9 للمتغير y نجد ان هناك مشكلة في البرنامج. فالبرنامج تعامل مع المدخلات على انها سلسلة نصية وتمت عملية الجمع كما تعلمنا سابقا بتجميع النصوص مع بعضها البعض. يمكن حل المشكلة هذه بتحويل سلسلة النصوص المدخلة الى ارقام عشرية باستخدام الدالة ()float كما في المثال التالي:
    x=input("please enter a value for x: ") y=input("please enter a value for y: ") sum=float(x)+float(y) print("The sum of x and y = ",sum)

    تمارين استكشافية

    ١- طور البرنامج السابق باستخدام حلقة while بحيث تجعل البرنامج يسأل المستخدم بعد اجراء عملية الجمع عما إذا كان يرغب في اجراء عملية جمع أخرى او يرغب في انهاء البرنامج. فاذا كانت الإجابة بـ yes فعندها يقوم باجراء عملية جمع اخرى اما اذا كانت الإجابة بـ no فان البرنامج يطبع رسالة "Good by"؟
    ٢- قم بكتابة برنامج يطلب من المستخدم تخمين رقم سري انت تختاره بين العددين 0 و 20 بحيث يقوم البرنامج بارشاد المستخدم الي الرقم السري بكتابة تلميح في كل مرة يدخل فيها رقما غير صحيح. فمثلا اذا ادخل المستخدم رقما اقل من الرقم السري يظهر البرنامج رسالة تخبره بان يختار رقما اكبر والعكس صيحيح اذا كان الرقم المدخل اكبر من الرقم السري؟ عندما يصل المستخدم للرقم السري الصحيح يطبع البرنامج رسالة تفيد بذلك وينهي البرنامج؟
    ٣- طور البرنامج الموجود في تمرين رقم 2 بحيث يعطي البرنامج 3 محاولات فقط للمستخدم للوصول للرقم السري الصحيح؟

    فتح الملفات

    ان البرامج السابقة لم تكن تمكننا من ادخال البيانات بشكل آلي ولم تكن تمكننا من الاحتفاظ بمخرجات البرنامج بعد ان ينتهي البرنامج من تنفيذ الكود البرمجي. لذلك كان لا بد ان يوجد المبرمجين تركيبا لغويا يمكن من التعامل مع الملفات بغرض القراءة منها اوالكتابة اليها. وفي لغة بايثون نجد ان التعامل مع الملفات يتم من خلال استخدام الدالة ()open والتي يكون تركيبها اللغوي على النحو التالي:
    open("filename","mode",buffer)
      حيث تحتوي الدالة على ثلاثة مدخلات احدهم الزامي والاخران اختياريان وهي على النحو التالي:
    • filename : مدخل الزامي يمثل اسم الملف المطلوب التعامل معه. اذا لم يكن اسم الملف موجود في نفس المجلد الذي يتم من خلاله تشغيل البرنامج فلابد من تحديد مسار الملف حتى يتسنى للبرنامج الوصول للملف. كما يجب ملاحظة ان كتابة اسم و مسار الملف دائما ما يكون على هيئة نص أي بمعنى آخر يجب ان يكون بين احدى علامات التنصيص التي يسمح بها مفسر بايثون.
    • Mode : مدخل اختياري يمثل نوع العملية المراد تنفيذها على الملف. وعند حذف قيمة هذا المدخل فان القيمة الافتراضية التي سوف يعتمدها مفسر بايثون هي عملية القراءة "r". والجدول التالي يمثل العمليات الاكثر استخداما عند التعامل مع الملفات:

    العملية قراءة ملف كتابة ملف اضافة بيانات الى آخر الملف mode "r" "w" "a"

  • buffer : مدخل اختياري يمثل كمية البيانات التي يراد قراءتها او كتابتها الى الملف. وعند حذف هذه القيمة عند استخدام الدالة ()open فان القيمة الافتراضية التي سوف يعتمدها مفسر بايثون هي قراءة كافة محتويات الملف.
  • من خلال استخدام التركيب السابق يصبح لدينا كائن في ذاكرة الكمبيوتر فقط تم تحديد نوع العملية المراد عملها من خلاله اما اجراء عملية القراءة والكتابة الفعلية لا تتم الا باستخدام دوال خاصة بالقراءة والكتابة والتي سوف نتعرف عليها بشئ من التفصيل في الجزئية التالية.
    اولا: عملية القراءة من الملفات
    يحتوي مجلد الكتاب هذا على مجلد آخر اسمه data يحوي ملفا نصيا اسمه days.txt كتبت فيه مجموعة من البيانات النصية. لنقم بفتح الملف هذا وطباعة ما بداخله من بيانات من خلال كتابة الكود البرمجي التالي:
    file_name=open("data/days.txt") data=file_name.read() print(data) file_name.close()
    عند تشغيل الكود السابق سوف يقوم مفسر بايثون بانشاء كائن برمجي داخل الذاكرة يمثل قناة تواصل بين القرص الصلب الذي يحوي الملف ومفسر بايثون ويتم اسناد هذا الكائن للمتغير file_name . لاحظ اننا احتجنا الى كتابة مسار الملف لان الملف الذي نحن بصدده لا يقع في نفس مجلد الكتاب مباشرة وانما يوجد في مجلد فرعي اسمه data . كما يجب ملاحظة ان مفسر بايثون تعرف على نوع العملية التي نريد القيام بها على الملف على الرغم من عدم تحديدها في مدخلات الدالة ()open وذلك لان القيمة الافتراضية لنوع العملية mode هي عملية القراءة "r" كما اشرنا سابقا. ومن ثم يقوم بتنفيذ عملية قراءة محتيويات الملف من خلال الدالة ()read ويقوم باسناد محتويات الملف للمتغير data واخيرا يقوم بطباعة محتويات الملف من خلال الدالة ()print واغلاق الكائن البرمجي ومسحة من الذاكرة من خلال الدالة ()close.
    كما يجب الإشارة الى ان هناك دوال أخرى تقوم بعملية القراءة من ملف ولكن بخصائص تختلف قليلاً عن الدالة ()read. فمثلاً يمكن استخدام الدالة ()readline لقراءة سطر واحد من الملف كما في المثال التالي:
    file=open("data/days.txt") line=file.readline() print(line)
    كما يمكن قراءة الملف السابق بأكمله باستخدام الدالة ()readlines (لاحظ s الجمع للتفريق بين الدالة السابقة والدالة التي نحن بصددها الآن) بحيث يكون ناتج القراءة عبارة عن قائمة من البيانات كل بيان فيها يمثل محتوى كل سطر من الملف الذي تتم قراءته كما في المثال التالي:
    file=open("data/days.txt") lines=file.readlines() print(lines)

    تمارين استكشافية

    1. قم بكتابة برنامج يقرأ من ملف نصي اسمه numbers.txt موجود في نفس المجلد data ويحتوي على الاعداد من 1 الى 10 في السطر الاول و 11 الى 20 في السطر الثاني و 21 الى 30 في السطر الثالث. حاول ان تقرأ محتويات الملف باستخدام الدوال الثلاث السابقة وطباعتها على الشاشة؟
    2. حاول التعرف على نوع البيانات الناتجة من عملية القراءة بالدوال الثلاث السابقة باستخدام الدالة ()type؟
    3. قم بفتح الملف countries.txt من المجلد data ثم قم بقراءة الملف وطباعة جميع أسماء الدول الموجودة في الملف؟
    4. طور البرنامج في التمرين 3 بحيث يقوم بطباعة الدول التي يزيد عدد حروفها عن 10 احرف؟
    5. طور البرنامج في التمرين 3 بحيث يقوم بطباعة الدول التي يقل عدد حروفها عن 6 احرف؟
    ثانيا: عملية الكتابة الى الملفات
    لنفرض الان اننا نريد الكتابة على ملف ماذا بوسعنا ان نغير في الدالة ()open للقيام بهذه المهمة؟ في حقيقة الامر ان الكتابة على ملف تتطلب فقط القيام بتغيير قيمة النمط (mode) الافتراضية في الدالة ()open والتي تمثل المدخل الثاني الذي يلي اسم الملف كما في المثال التالي:
    file=open("data/weeks.txt","w")
    عند كتابة “w” كمدخل ثاني في الدالة ()open فان مفسر بايثون سوف يقوم بإنشاء كائن ارتباط للكتابة بدلاً من كائن القراءة الافتراضي. لكن يجب ملاحظة ان هناك اختلاف بسيط بين نمطي الكتابة والقراءة. فنمط الكتابة عند عدم وجود الملف المحدد فان مفسر بايثون يقوم بإنشائه تلقائيا بينما نمط القراءة يعطي رسالة بوجود خطأ في حال عدم العثور على الملف.
    ودالة الكتابة في بايثون هي ()write. ويتم التعامل معها تقريبا بنفس الطريقة التي استخدمناها مع دالة القراءة كما هو موضح في المثال التالي:
    file=open("data/weeks.txt","w") file.write("1st week 2nd week 3rd week 4th week") file.close()
    فبعد انشاء كائن الارتباط للكتابة file نستخدم الدالة ()write لتنفيذ عملية الكتابة على الملف والتي هي هنا عبارة عن نص وضع بين قوسي الدالة ()write. ويمكن كذلك استخدام الدالة ()writelines وذلك لكتابة قائمة من البيانات كما في المثال التالي:
    file=open("data/greeting.txt","w") file.writelines(["Welcome ","to ", "python ", "world!"])
    يجب ملاحظة ان فتح ملف بغرض الكتابة عليه يختلف بحسب النمط المدخل. فالنمط “w” يعنى مسح محتويات الملف السابقة ان وجدت والكتابة على الملف من بدايته. اما إذا استخدمنا النمط “a” كمدخل ثاني في الدالة ()open فان ذلك يعنى ان محتويات الملف السابقة لا يتم مسحها وانما تتم الكتابة من حيث ما انتهى محتوى الملف السابق. وهناك أنماط أخرى لا يسع الحديث عنها هنا ويمكن البحث عنها من خلال محركات البحث للاستزادة.

    تمارين استكشافية

    1- لتاكد ان عمليتي الكتابة في المثالين السابقين تمتا بشكل صحيح. قم بتنفيذ كل كود ومن ثم قم بكتابة كود برمجي اخر يقوم بقراءة وطباعة ما تم كتابته؟
    2- قم بفتح الملف countries.txt وحاول كتابة عدد الدول في نهاية الملف؟
    ان فتح ملف سواء للقراءة او الكتابة بالطريقة السابقة قد يسبب بعض إشكالية في نفاذ جزء من مساحة الذاكرة وذلك لان الملف الذي تم فتحه سوف يظل في ذاكرة الكمبيوتر حتى يتم اغلاقه. لذلك ان استخدام الطريقة السابقة للتعامل مع الملفات يتطلب تنظيف الذاكرة بعد الانتهاء من عملية القراءة او الكتابة باستخدام الدالة ()close كما في المثال التالي:
    file=open("data/weeks.txt") content=file.read() print(content) file.close()
    ولتلافي نسيان اغلاق الملفات بعد الانتهاء من اجراء العمليات عليها يمكن استخدام تركيب لغوي آخر في لغة بايثون يعتمد على استخدام البادئة with والذي من خلاله يقوم مفسر بايثون بإغلاق الملف المفتوح بمجرد الانتهاء من هذه المهمة كما في المثال التالي:
    with open("data/weeks.txt") as file: content=file.read() print(content)
    لاحظ ان في المثال السابق تم استخدام الاتي:
    • بدء التركيب اللغوي باستخدام كلمة with.
    • استخدام كلمة as لإسناد كائن الربط الى المتغير file.
    • استخدام النقطتين فوق بعض ":" عند نهاية السطر الاول.
    • ترك مسافة في الاسطر التي تلي السطر الاول للقيام بالعمليات المطلوبة على الملف المفتوح.
    • يتم اقفال كائن الارتباط بعد الانتهاء من اخر سطر في الاسطر المبدوءة بترك مسافة.

    تمارين استكشافية

    1- قم باعادة كتابة تمارين القراءة والكتابة السابقة باستخدام التركيب اللغوي with؟

    الفصل الثامن : التعامل مع المكتبات


    في هذا الفصل سوف نتطرق الى الحديث عن المكتبات (modules) في لغة بايثون والتي تعتبر من أحد أهم العوامل التي ساهمت في ذياع صيت لغة بايثون على كافة الأصعدة. فعلى سبيل المثال نجد مكتبات مثل numpy و scipy وmatplotlib مشهورة الاستخدام في الاوساط العلمية والبحثية. وكذلك مكتبتي Django و flask التي يعتمد عليها المبرمجين في بناء تطبيقات الويب. وتعتبر مكتبة pandas مشهورة لدى المهتمين بتحليل البيانات الى غير ذلك من المكتبات الاخرى. سنحاول في هذا الفصل تبسط مفهوم المكتبات في بايثون لدى القارئ حيث سنبدأ بتعريف المكتبات وكيفية التعامل معها. وسنقوم أيضاً بتعريف القارئ ببعض المكتبات المبنية داخل اصدارة بايثون 3 والتي يكاد لا يستغنى عنها عند كتابة أي كود برمجي. كما سنتعرف ايضا على كيفية تنصيب مكتبة خارجية الى مجلد مكتبات بايثون من اجل استخدامها في كتابة البرامج. وفي نهاية الفصل سوف نتطرق بشكل مبسط الى كيفية انشاء مكتبة خاصة بنا.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. تعريف المكتبات في لغة بايثون وكيفية استدعائها.
    2. استخدام بعض المكتبات المبنية داخل اصدارة بايثون.
    3. معرفة كيفية تنصيب مكتبة خارجية داخل مجلد مكتبات بايثون لاستخدامها في كتابة البرامج.
    4. التعرف على كيفية بناء مكتبة خاصة بنا.

    تعريف المكتبة في بايثون

    المكتبة في بايثون هي عبارة عن ملف بايثون اعتيادي يحتوي على كود برمجي تم من خلاله تعريف عدد من الكائنات البرمجية والدوال والمتغيرات بهدف تسهيل أداء مهام متعلقة بموضوع معين. فنجد على سبيل المثال المكتبة math والتي هي عبارة مكتبة داخلية مبنية في اصدارة بايثون تحوي على دوال رياضية عديدة كدالة الجذر التربيعي ()sqrt وادالة جيب الزاوية ()sin وجيب تمام الزاوية ()cos الى غيرها من الدوال الرياضية. ولكي نتمكن من استخدام الدوال الموجودة داخل أي مكتبة يجب ان يتم استدعاؤها من خلال الامر import كما في المثال التالي:
    import math x=math.sqrt(4) print(x)
    لاحظ ان عملية الاستدعاء تمت باستخدام الامر import متبوعاً باسم المكتبة. ومن ثم أصبح بإمكاننا استخدام الدوال داخل المكتبة math باستخدام اسم المكتبة أولاً ومن ثم اسم الدالة بعد الفصل بينها بنقطة. وطريقة الاستدعاء هذه ليست الوحيدة في بايثون. فهناك طرق استدعاء أخرى. فمثلا يمكن استدعاء المكتبة السابقة باستخدام التركيب اللغوي التالي:
    from math import * x=sqrt(4) print(x)
    لاحظ ان طريقة الاستدعاء في المثال السابق يمكن ترجمتها الى المعنى التالي: "من المكتبة math استورد كل الدوال والمتغيرات." وعلامة النجمة بعد كلمة import تعني استيراد كل شيء. كما يجب ملاحظة انه عند استخدام طريقة الاستدعاء هذه فإننا لا نستخدم اسم المكتبة قبل الدالة وانما نكتفى باستخدام الدالة مباشرة. لكن في حقيقة الامر فان مبرمجي بايثون لا يوصون باستخدام طريقة الاستدعاء هذه لسببين: اولها ان هذه الطريقة تستدعي كل شيء في المكتبة والتي قد لا نحتاج الا لجزء يسير من الدوال الموجود في المكتبة. فتؤدي هذه الطريقة الى حشو ذاكرة الكمبيوتر بما لا فائدة منه. ثاني هذه الاسباب هو انه قد يحصل تضارب بين اسماء الدوال الموجودة في المكتبة المستدعاه وأسماء دوال جديدة يمكن ان نقوم بإنشائها بأنفسنا. فمثلا عندما نستورد المكتبة math بالطريقة السابقة ثم نقوم بتعريف دالة جديدة تحمل اسم أحد الدوال الموجودة في المكتبة math فانه يحصل ارتباك لدى المبرمج حول اية دالة تم استخدامها عند تنفيذ البرنامج كما في المثال التالي:
    from math import * def sqrt(number): return "Hello " + str(number) x=sqrt(4) print(x)
    على الرغم ان من يحدد ايتها دالة سوف يستدعيها مفسر بايثون عند تنفيذه الكود البرمجي السابق محكوم بمكان تعريف الدالة الجديدة التي قمنا بإنشائها داخل الكود البرمجي الا ان طريقة الاستدعاء هذه تسبب ارباك لكل من يقرأ الكود البرمجي السابق. لذلك يفضل تحديد الدوال المراد استخدامها حين الاستدعاء بحيث لا يتم استدعاء دوال المكتبة بالكامل بل يكتفى باستدعاء الدوال المحددة فقط كما في المثال التالي:
    from math import sin, pi print(sin(0.5*pi))
    كما يمكن تغيير اسم المكتبة حين الاستدعاء بغرض تبسيط كتابة الكود البرمجي كما في المثال التالي:
    import math as m print(m.sin(0.5*m.pi))

    المكتبات المدمجة مع اصدارة بايثون

    تحتوي اصدارة بايثون 3 على العديد من المكتبات الداخلية (built-in modules). وسوف نتطرق في هذه الجزئية من الفصل الي ثلاثة من هذه المكتبات وهي os و sys و أخيرا time. اما بقية المكتبات فيمكن استخدام الدالة ()help والتي تعطي تعريف وشروحات وافيه عن أي مكتبة داخلية تكتب داخل قوسي الدالة كما في المثال التالي:
    import random help(random)
    كما يمكن أيضا التعرف على محتويات أي مكتبة بعد استدعائها باستخدام الدالة ()dir كما في المثال التالي:
    import math print(dir(math))
    لاحظ ان ناتج الدالة ()dir هو عبارة قائمة تحوي جميع دوال ومتغيرات المكتبة math. ولو دققت النظر في بيانات القائمة السابقة لوجدت ان الدوال التي تعلمناها من المكتبة math موجودة بالفعل داخل هذه القائمة.
    كما يمكن معرفة محتويات اي مكتبة مدمجة من خلال استخدام خلايا الكود في هذا الكتاب. فعند كتابة الامر import والضغط على الزر tab من لوحة المفاتيح يقوم جوبتر نوت بوك باقتراح قائمة بالمكتبات التي يمكنك ان تستدعيها. وكلما كتبت احرف اكثر من اسم المكتبة التي تريد استدعاؤها كلما كانت اقتراحات جوبتر نوت بوك اكثر دقة لما تنوي استدعاؤه. كما ان خاصية الاكمال الذاتي هذه تساعد المبرمج على مشاهدة محتويات المكتبة من دوال ومتغيرات. فبمجرد كتابة اسم المكتبة واضافة نقطة بعدها ثم الضغط على زر tab من لوحة المفاتيح يبدأ جوبتر بعرض الدوال والمتغيرات التي تحتويها المكتبة لكي تساعد المبرمج على العثور على ما يبحث عنه.

    مكتبة os

    تسهل مكتبة os التفاعل مع نظام التشغيل الذي يعمل عليه المبرمج سواء كان ذلك نظام ويندوز او ماك او لينكس. وتقدم هذه المكتبة الكثير من الدوال الخاصة بالتعامل مع نظام التشغيل فمثلا يمكن معرفة المجلد الحالي الذي يتواجد فيه البرنامج والذي نعمل على كتابة كوده باستخدام الدالة ()getcwd كما في المثال التالي:
    import os print(os.getcwd())
    كما يمكن أيضا تنفيذ جميع الاوامر التي يمكن تنفيذها على محرر الاوامر في ويندوز او ماك او لينكس من خلال كود بايثون البرمجي باستخدام الدالة ()system بحيث يوضع الامر المراد تنفيذه بين علامتي تنصيص داخل قوسي الدالة كما في المثال التالي:
    import os os.system("notepad")
    وكذلك أيضا يمكن تكوين مجلد باستخدام الدالة ()mkdir وكذلك حذف مجلد باستخدام ()rmdir وايضاً يمكن تغيير موقع المجلد باستخدام الدالة ()chdir وعرض قائمة بأسماء المجلدات باستخدام الدالة ()listdir والكثير الكثير من الدوال الخاصة بالتعامل مع الملفات والمجلدات والتي لا يسع المقام لذكرها هنا ويمكن الاطلاع عليها من خلال وسائل المساعدة التي ذكرناه سابقاً.

    مكتبة sys

    تمكن مكتبة sys المبرمج بلغة بايثون من التعامل مع كل ما يخص مفسر بايثون. فمثلا يمكن معرفة رقم اصدارة بايثون عن طريق استخدام المتغير version كما في المثال التالي:
    import sys print(sys.version)
    كما يمكن معرفة المجلدات التي سوف يقوم مفسر بايثون بالبحث فيها عند اجراء اي عملية استدعاء سواءً كانت لمكتبة او دالة او فتح لملف وذلك باستخدام المتغير path كما في المثال التالي:
    import sys print(sys.path)
    يجب ملاحظة ان ناتج الكود السابق يختلف بحسب اختلاف إعدادات النظام لكل مستخدم وطريقة ونوعية تنصيبه لإصدارة بايثون. فأول مكان يقوم مفسر بايثون بالبحث فيه هو المجلد الذي تم تشغيل الكود البرمجي منه ثم مجلد المكتبات الداخلية (يختلف باختلاف إصدارة بايثون المستخدمة) ومن ثم مجلد المكتبات الخارجية site-packages.
    كما يمكن استخدام المتغير argv في مكتبة sys للتعرف على المدخلات التي تصاحب ملف بايثون عند تشغيله من محرر أوامر النظام الذي يعمل عليه المستخدم. ولتوضح هذه الفكرة أكثر انشاءنا داخل مجلد كتابنا هذا ملف بايثون اسمه test.py وكتبنا بداخله الكود البرمجي التالي:
    import sys print(sys.argv)
    لنقم الان بتشغيل الملف السابق من محرر الاوامر سواءً كان ذلك في ويندوز او في غيره من الانظمة. وللقيام بذلك من خلال خلية كود في هذا الكتاب سوف نستخدم علامة التعجب ! قبل امر تشغيل الملف كما في المثال التالي:
    !python test.py
    بعد كتابة الامر python test.py والضغط على زر الادخال يتم تشغيل الملف الذي كتبناه بلغة بايثون والذي يقوم بطباعة قيمة المتغير argv. المتغير argv الذي تم طباعته هنا هو عبارة عن قائمة تحتوي على اسم الملف الذي قمنا بتشغيله. ويمكن إضافة بيانات الى المتغير argv وذلك بكتابتها بعد اسم الملف عند التشغيل كما في المثال التالي:
    !python test.py 1 2 3
    لا حظ ان كل شيء يكتب بعد اسم الملف حين تشغيله يتعامل معه مفسر بايثون على انه مدخل يتم اضافته الى قائمة argv ويجب ملاحظة ان الفصل بين البيانات يتم بواسطة ترك مسافة بين كل مدخل وآخر كما هو واضح في المثال السابق. وتعتبر طريقة الادخال هذه شائعة الاستخدام للبرامج التي يتم تشغيلها من محرر أوامر النظام.

    تمارين استكشافية

    ١- قم بكتابة برنامج يستغل خاصية ادخال البيانات عند التشغيل بحيث يقوم بجمع الاعداد المدخلة وطباعة ناتج الجمع على شاشة الكمبيوتر؟

    مكتبة time

    مكتبة time الداخلية تحتوي على العديد من الدوال التي تساعد المبرمج على القيام بمهام متعلقة بالوقت. فمثلا يمكن استخدام الدالة ()time لمعرفة الوقت بعدد الثوان التي مضت منذ الساعة 12 صباحاً من اليوم الاول لشهر يناير لعالم 1970 كما في المثال التالي:
    import time time.time()
    قد لا تكون صيغة الوقت هذه مفيدة للكثير لكن يمكن تحويل هذه الثواني لصيغة وقت مألوفة باستخدام الدالة ()ctime كما في المثال التالي:
    import time t=time.time() time.ctime(t)
    الدالة ()ctime تعطي صيغة الوقت المألوفة عندما نزودها بعدد الثوان التي مرت منذ ذلك التاريخ لكن عندما نستخدمها بدون أي مدخلات فإنها تعطينا الوقت الحالي بالصيغة المألوفة كما في المثال التالي:
    import time print(time.ctime())
    كما يمكن تأجيل تنفيذ أي سطر برمجي يقع بعد الدالة ()sleep لعدد معين من الثوان توضع بين قوسي الدالة كما في المثال التالي:
    import time print(time.ctime()) time.sleep(5) print(time.ctime())
    كما يجب عليك ملاحظة ان معظم دوال المكتبة time تتعامل مع كائن برمجي للوقت ينتج من تنفيذ بعض الدوال بحيث يكون تكوين هذا الكائن البرمجي مجزأ الى أجزاء الوقت المعروفة من سنة وشهر ويوم وساعة الى ...الخ. بحيث تسهل على المبرمج التعامل مع أجزاء الوقت وكتابتها بالطريقة التي يرغب بها. فعند استدعاء الدالة ()localtime يتم ارجاع كائن الوقت الخاص بالمكان الحالي وكذلك الدالة ()gmtime تقوم بارجاع كائن خاص بوقت جرينتش كما في المثال التالي:
    import time Makkah_time=time.localtime() London_time=time.gmtime() print(Makkah_time) print(London_time) print(Makkah_time.tm_hour)

    تمارين استكشافية

    ١- قم بكتابة برنامج يقوم بحساب فارق التوقيت بين مكة ولندن وطباعة الناتج على شاشة الكمبيوتر؟
    كما يمكن إعادة صياغة الوقت بالطريقة التي نرغب بها باستخدام الدالة ()strftime كما في المثال التالي:
    import time Makkah_time = time.localtime() formatted_time = time.strftime("%m/%d/%Y, %H:%M:%S", Makkah_time) print(formatted_time)
    في المثال السابق تم وضع صيغة الوقت المطلوبة على شكل نص كمدخل اول للدالة وكائن الوقت وضع كمدخل ثان. ولحل شفرة رموز شفرة الوقت السابقة يمكن الاستعانة بالجدول التالي:
    المعنىالرمز
    السنة من 4 ارقام%Y
    السنة في رقمين%y
    الشهر%m
    اليوم%d
    الساعة%H
    الدقيقة%M
    الثانية%S

    طريقة تنصيب مكتبة خارجية

    في الإصدارات الحديثة من بايثون تم دمج الأداة المستخدمة لإضافة المكتبات الخارجية مع مفسر بايثون بحيث ان هذه الأداة تكون متوفرة بمجرد تنصيب اصدارة بايثون. تختلف هذه الأداة بحسب التوزيعة المستخدمة. فالأداة pip تأتي مع الاصدارة الأساسية. اما توزيعة Anaconda فلها أداة تنصيب خاصة بها تسمى conda تؤدي نفس وظائف الأداة pip. ولشرح طريقة استخدام pip لنستعرض أولا المكتبات التي تم تنصيبها داخل اصدارة بايثون وذلك باستخدام الامر pip list من محرر أوامر النظام كما في المثال التالي:
    !pip list
    فالمثال السابق قام بطباعة كافة المكتبات المنصبة على اصدارة بايثون وارقام اصداراتها.
    فاذا كانت المكتبة التي ترغب باستخدامها ليست موجود في هذه القائمة فان بإمكانك تنصيبها عن طريق استخدام الامر pip install متبوعاً باسم المكتبة المطلوبة كما في المثال التالي:
    !pip install progressbar
    في المثال السابق قمنا بتنصيب المكتبة progressbar والتي تعتبر من المكتبات الصغيرة بحيث تساعد المبرمج في تكوين كائن مرئ يوضح مدى التقدم في انجاز مهمة ما. وعند اكتمال التنصيب تعطي الأداة pip رسالة بنجاح عملية التنصيب للمكتبة. المثال التالي يوضح طريقة استخدام هذه المكتبة بعد تنصيبها:
    from time import sleep from progressbar import ProgressBar bar=ProgressBar() for i in bar(range(50)): sleep(0.5)
    اما إذا كانت المكتبة منصبة لديك من قبل وترغب فقط بتحديثها فانه بإمكانك استخدام خيار التحديث كما في المثال التالي:
    pip install --upgrade progressbar
    حيث اعطانا الامر السابق ان مكتبة progressbar المنصبة على هي أحدث اصدارة موجودة ولا تحتاج الى تحديث. اما إذا كانت الاصدارة قديمة فان pip سوف يقوم بتنصيب اصدارة المكتبة الاحدث من موقع https://pypi.org. اما الأداة conda فإنها تعمل تقريبا بنفس الطريق التي تعمل بها الأداة pip. لذلك اعتقد انه لا داعي للحديث عنها هنا ويمكن استخدام محرك البحث قوقل لتعرف عليها أكثر.

    طريقة انشاء مكتبة

    كما ذكرنا في بداية الفصل مكتبات بايثون هي مجرد ملف بايثون اعتيادي ينتهي بالحرفين py. ويحتوي على كائنات برمجية ودوال ومتغيرات خاصة بموضوع معين. ولكي نوضح سهولة طريقة انشاء أي مكتبة لنفرض اننا بصدد انشاء مكتبة خاصة بتحويل الوحدات الطولية (متر، سنتيمتر، مليمتر، مايكرومتر) ولنفرض ان اسمها meter. للقيام بهذه المهمة سوف نفتح ملف جديد ونحفظه باسم meter.py ثم نبدأ بكتابة الكود البرمجي التالي بداخله:
    '''This module has been built to convert between the different units of length''' def to_cm(number): '''This function converts meter to centimeter''' return number*100 def to_mm(number): '''This function converts meter to millimeter''' return number*1000 def to_micro(number): '''This function converts meter to micrometer''' return number*10e6 def from_cm(number): '''This function converts centimeter to meter''' return number/100 def from_mm(number): '''This function converts millimeter to meter''' return number/1000 def from_micro(number): '''This function converts micrometer to meter''' return number/10e6
    الآن لنفتح ملف بايثون جديد ونحاول استدعاء المكتبة meter التي قمنا بإنشائها. يجب ملاحظة ان ملف المكتبة التي قمنا بإنشائها والملف الذي سوف نستدعيه من خلاله المكتبة يجب ان يكونا في مجلد واحد لكي يعثر مفسر بايثون على المكتبة كما تعلمنا سابقاً حين استعرضنا مكتبة sys ودالتها ()path.
    import meter meter.to_cm(4)
    كما تلاحظ من المثال السابق ان المكتبة التي قمنا بإنشائها تعمل كأي مكتبة موجودة داخل اصدارة بايثون. فمثلا يمكن ان نتعرف على الشروحات التي كتبناها عن المكتبة بين علامة التنصيص الثلاثية باستخدام المتغير __doc__ كما يلي:
    import meter meter.__doc__
    كما يمكن استعراض محتويات المكتبة meter باستخدام الدالة ()dir كما يلي:
    import meter dir(meter)
    لاحظ ان المتغيرات التي تبتدأ بشرطتين وتنتهي بشرطتين لم نقم بكتابتها داخل المكتبة ولكن بايثون قام بإنشائها بشكل تلقائي. كما يجب ملاحظة ان ملف المكتبة التي انشأناها ليس بالضرورة ان يكون في نفس المجلد الذي سوف نستدعي منه المكتبة. فعندما نرغب في وضع المكتبة في مكان اخر وليكن في المجلد modules الموجود في مجلد الكتاب هذا فانه يجب علينا ان نخبر مفسر بايثون بان يبحث في المجلد الذي وضعت فيه المكتبة وذلك من خلال إضافة هذا المجلد الي القائمة التي سوف يبحث فيها مفسر بايثون عن المكتبة كما في المثال التالي:
    import sys sys.path.append('/units') import meter print(meter.to_cm(5))
    لاحظ اننا قمنا بإضافة المجلد الذي يحتوي على المكتبة meter الى قائمة الاماكن التي يبحث فيها مفسر بايثون عن المكتبات بواسطة الدالة ()append قبل ان نقوم باستدعاء المكتبة meter.
    كما يمكن أيضا إضافة المكتبة الى مجلد خاص تم تحديده من قبل مفسر بايثون ليكون خاص بمكتبات المستخدم. ولتعرف على هذا المجلد نقوم باستدعاء المتغير USER_SITE من مكتبة site الداخلية كما في المثال التالي:
    import site print(site.USER_SITE)
    المجلد الذي ينتج عن تنفيذ الكود السابق يختلف باختلاف اصدارة بايثون المستخدمة ويختلف أيضا باختلاف نظام التشغيل المستخدم. وفي غالب الأحيان لا يكون هذا المجلد موجود ويجب علينا انشاءه ومن ثم وضع المكتبة التي انشأناها بداخله لكي يتم التعرف عليها.

    تمارين استكشافية

    ١- قم بتطوير مكتبة meter لكي تقوم بالتحويل من سنتيمتر الى ميليمتر والعكس ومن سنتيمتر الى مايكرومتر والعكس؟
    وقبل ان نختم هذا الفصل نود نبين انه يمكن عمل رزمة من المكتبات package والتي هي عبارة عن مجموعة من المكتبات التي يحويها مجلد واحد. فعند انشاء مكتبة جديدة لتحويل درجات الحرارة وليكن اسمها مثلا temperature.py بحيث توضع هذه المكتبة الي جانب المكتبة meter السابقة في مجلد واحد وليكن اسم المجلد units فان مفسر بايثون يشترط للتعرف على هذه المكتبات ان يقوم المبرمج بإنشاء ملف فارغ داخل هذا المجلد بحيث يكون اسم هذا الملف __init__.py كما هو موضح بالشكل التالي:

    image.png

    كما يجب ملاحظة ان الشروط اللازمة لعثور مفسر بايثون على مكتبة معينة تنطبق على رزمة المكتبات كما وضحنا سابقاً. ويمكن استدعاء المكتبة meter من رزمة المكتبات units كما يلي:
    import sys sys.path.append('units') from units import meter print(meter.from_cm(100))

    الفصل التاسع : البرمجة الكائنية في بايثون


    في الفصول السابقة كنا نعتمد في كتابتنا للبرامج على الأسلوب التسلسلي والذي عادة ما يكون مناسبا لكتابة البرامج الصغيرة فقط. لكن عندما يراد كتابة برامج كبيرة متعددة المهام فان استخدام الأسلوب التسلسلي يصبح غير مجد وذلك لسببين:
    أولا: تصبح عملية التطوير شاقة عندما يراد إضافة مزايا ومهام جديدة في البرنامج.
    ثانيا: ترابط الكود بشكل تسلسلي يجعل عملية اصلاح المشاكل في البرنامج غاية في الصعوبة وذلك لان اصلاح واضافة جزء من البرنامج قد يترتب عليه احداث مشكال جديدة في أجزاء أخرى من البرنامج.
    لذلك سوف نتطرق في هذا الفصل الى التعرف على أسلوب جديد في كتابة الأكواد البرمجية يدعى البرمجة الكائنية Object Oriented Programming. يمتاز هذا الأسلوب البرمجي عن الأسلوب التسلسلي في تجزئته للبرنامج الى مكونات برمجية صغيرة لها خصائص ومهام يتم كتابتها داخل تركيب برمجي محدد يدعى كائن برمجي class.

    أهداف الفصل

    عند اتمام هذا الفصل يجب ان يكون لديك المام بالآتي:
    1. التعرف على كيفية انشاء كائن برمجي (class) له خواص ومهام محددة.
    2. التعرف على كيفية انشاء نسخ من الكائن البرمجي لأداء مهام برمجية تم كتابتها داخل تعريف الكائن البرمجي.
    3. معرفة كيفية توريث الخصائص والمهام بين الكائنات البرمجية.

    الكائنات البرمجية Classes

    في فصول سابقة تعرفنا على أنواع البيانات وطريقة التعامل معها. ما لم نخبرك به عن هذه البيانات هو ان كل نوع من هذه البيانات يمثل كائن برمجي في بايثون تم إنشاؤه بالطريقة التي سوف نتعلمها في هذا الفصل. فالبيانات النصية مثلا تمثل كائن برمجي محدد تم تعريفه داخل اصدارة بايثون وكنا نتعرف على نوع الكائن بواسطة الدالة ()type كما في المثال التالي:
    print(type('hello'))
    لاحظ ان النصوص عبارة عن كائن برمجي من النوع str تم إنشاؤه داخل مكتبة بايثون القياسية. ولتعرف على هذا الكائن وكيفية كتابته وما يحتويه من أكواد برمجية يكمن الاستعانة بالدالة ()help وكتابة str بدون علامات اقتباس بين قوسي الدالة كما يلي:
    help(str)

    تمارين استكشافية

    1- تعرف على الكائنات البرمجية للاعداد الموجود في بايثون باستخدام الدالة ()type و ()help؟
    يكمن انشاء كائن برمجي جديد في بايثون بنفس الأسلوب الذي استخدمناه لإنشاء الدوال في الفصل السابع الا اننا هنا سوف نستخدم أداة التعريف class بدلا عن def كما في المثال التالي:
    class Point: pass
    لاحظ اننا قمنا بكتابة اسم الكائن Point بعد أداة تعريف الكائن class وأنهينا السطر بنقطتين فوق بعض. اسم الكائن هنا يمكن ان يكون أي اسم تختاره شريطة ان تنطبق عليه جميع شروط المتغيرات التي ذكرناها في الفصل الاول. لاحظ أيضا اننا استخدمنا خاصية ترك المسافة التي تعلمناها سابقا عند تعريف الدوال واستخدمنا الامر pass لنخبر مفسر بايثون اننا نود فقط انشاء الكائن بحيث نستطيع لاحقا إضافة خواص ومهام لهذا الكائن. ولتأكد من صحة انشائنا للكائن السابق يمكن القيام بكتابة اسم الكائن كما يلي:
    print(Point)
    ويمكننا أنشاء نسخة من الكائن Point واسنادها للمتغير p1 باستخدام اسم الكائن متبوعا بقوسين فارغين كما في المثال التالي:
    p1=Point()
    وعند طباعة نوع المتغير p1 فان مفسر بايثون سوف يعطينا انه من الصنف Point كما يلي:
    p1=Point() type(p1)
    لنفرض الان اننا نريد ان نقوم باعطاء هذا الكائن بعض الخواص (attributes). ما نقصده بالخواص هنا هو مجرد متغيرات تم كتابتها داخل تعريف الكائن البرمجي كما في المثال التالي:
    class Point(): x=0 y=1 print(Point.x) print(Point.y)
    لاحظ اننا اضفنا للكائن Point خاصية x وخاصية y وتم استدعاء هذه الخاصية باستخدام اسم الخاصية بعد اسم الكائن مفصولا بينهما بنقطة. هذا النوع من الخواص يسمى خواص الكائن البرمجي العامة. ويوجد نوع اخر من الخواص يسمى خواص النسخة التي يراد عملها من الكائن البرمجي ويتم كتابة هذه الخواص داخل دالة خاصة تسمى ()__init__ يتم تعريفها داخل الكائن بحيث يتم تنفيذها عند بداية انشاء أي نسخة من الكائن. فمثلا لنفرض اننا نريد ان ننشئ النسختين p1 و p2 من الكائن Point بحيث تكون قيمة الخاصية y مختلفة لكل نسخة من p1 و p2. للقيام بذلك نقوم بتعريف دالة خاصة تسمى ()__init__ كما يلي:
    class Point(): x=0 y=1 def __init__(self,x,y): self.x=x self.y=y p1=Point(5,4) p2=Point(4,2) print(p1.y) print(p2.y)
    هناك مجموعة ملاحظات يجب تدوينها حول المثال السابق :
    أولا: الدوال عندما تكتب داخل تعريف الكائن تسمى مهام methods.
    ثانيا: self في المثال السابق تشير الى اسم النسخة التي يتم عملها أيا كان اسم هذه النسخة. فكتابة self.y يتم ترجمته الى p1.y عندما يتم عمل النسخة p1 من الكائن Point.
    ثالثا: عند تعريف أي مهمة method داخل كائن فانه يجب كتابة المدخل self كأول قيمة لهذه المهمة.
    رابعا: عند عمل نسخة من الكائن لا نحتاج لكتابة self بين قوسي اسم الكائن ويكتفى فقط بالمدخلات الإلزامية التي تلي المدخل self في المهمة ()__init__. الفشل في تزويد المدخلات الإلزامية التي تلي self في المهمة ()__init__ يؤدي لظهور ملاحظة بوجود خطأ من قبل مفسر بايثون. فلو قمنا بعمل نسخة من الكائن Point بدون إعطاء قيمة للخاصية y فان مفسر بايثون يعطينا الملاحظة التالية:
    class Point(): x=0 y=1 def __init__(self,x,y): self.x=x self.y=y p1=Point() print(p1.y)
    لنقم الان بتعريف مهمة يمكن تنفيذها على النسخ التي يتم انشاؤها من هذا الكائن ولتكن هذه الوظيفة هي ايجاد طول الخط الذي يصل بين نقطة ونقطة الاصل. لتعريف هذه المهمة نقوم بانشاء دالة بنفس الطريقة التي تعلمنها في الفصل السابع ولكنها تختلف قليلا عن الدالة الاعتيادية في كونها تكتب داخل كائن وتحتوي على مدخل الزامي اول self كما في المثال التالي:
    class Point(): x=0 y=1 def __init__(self,x,y): self.x=x self.y=y def distance(self): return (self.x**2+self.y**2)**0.5
    pt1=Point(3,4) dist=pt1.distance() print(dist)
    لنقم الان بتعريف مهمة جديدة تقوم بحساب المسافة بين نقطة ونقطة اخرى وليس بين بالضرورة ان تكون هذه النقطة هي نقطة الاصل. للقيام بذلك نعرف دالة جديدة داخل الكائن على النحو التالي:
    class Point(): x=0 y=1 def __init__(self,x,y): self.x=x self.y=y def distance(self): return (self.x**2+self.y**2)**0.5 def two_point_distance(self,pt): return ((self.x-pt.x)**2+(self.y-pt.y)**2)**0.5
    point1=Point(3,4) point2=Point(3,4) dist=point1.two_point_distance(point2) print(dist)
    لنقم الان بطباعة نقطة تم نسخها من الكائن Point لنرى كيف يقوم بايثون بطباعة هذه النسخة.
    pt1=Point(1,2) print(pt1)
    لنقم بتحسين عملية طباعة النقطة السابقة بحيث يكون ناتج الطباعة (1,2) بدلا من القيمة الافتراضية التي يقوم بها مفسر بايثون حينما لا يجد مهمة داخل الكائن ترشده الي كيفية الطباعة. يوجود في بايثون مهمة خاصة تحمل الاسم ()__str__ يقوم مفسر بايثون بتنفيذها عندما نستخدم امر الطباعة ()print ولتعديل طباعة النقطة سوف نكتب المهمة ()__str__ على النحو التالي:
    class Point(): x=0 y=1 def __init__(self,x,y): self.x=x self.y=y def distance(self): return (self.x**2+self.y**2)**0.5 def two_point_distance(self,pt): return ((self.x-pt.x)**2+(self.y-pt.y)**2)**0.5 def __str__(self): return '({0}, {1})'.format(self.x, self.y)
    p1=Point(1,2) print(p1)