مبتدئ → متوسط · محتوى عربي على المنصة

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

دروس عربية كاملة على المنصة: شرح + مثال + محرر تشغيل فوري + تمرين. بلا الحاجة لفتح كورس أجنبي أولاً.

كيف يعمل المسار؟

  1. اقرأ الشرح العربي داخل الصفحة.
  2. شغّل المثال في المحرر (بايثون داخل المتصفح).
  3. نفّذ المهمة القصيرة وعدّل الكود.
  4. أجب عن السؤال وسجّل إكمال الدرس.

المصادر العربية المفتوحة التي نستعين بها مذكورة في المكتبة مع تراخيصها.

1. ما هي بايثون ولماذا نتعلّمها؟

الهدف: تعرف أين تُستخدم بايثون وكيف ينفّذ المفسّر البرنامج سطراً بعد سطر، وتشغّل أول برنامج وتقرأ أول رسالة خطأ.

بايثون (Python) لغة برمجة عامة الاستخدام ظهرت في مطلع التسعينيات، وتملك حقوقها اليوم مؤسسة برمجيات بايثون (Python Software Foundation)، وهي منظمة مستقلة غير ربحية، بينما يطوّرها مجتمع واسع من المتطوعين. اللغة مفتوحة المصدر ومجانية، وتعمل على ويندوز وماك ولينكس. تُستخدم في أتمتة المهام المكتبية، وتحليل البيانات، وبناء خوادم الويب، وتدريب نماذج تعلّم الآلة، وكتابة أدوات صغيرة يستعملها شخص واحد في عمله اليومي.

تخيّل مريم، طالبة محاسبة في المنصورة تعمل مساءً في مكتبة لبيع الأدوات المدرسية. كل ليلة تجمع مبيعات اليوم من دفتر ورقي بالآلة الحاسبة، ثم تنقل الإجمالي إلى جدول. بعد أسابيع قليلة من تعلّم بايثون كتبت برنامجاً من عشرة أسطر يجمع قائمة المبيعات ويطبع الإجمالي والصنف الأكثر بيعاً. البرنامج بسيط، لكنه يوفّر عليها ربع ساعة كل يوم ويمنع أخطاء الجمع اليدوي. هذا هو المدخل الواقعي لبايثون: مشكلات صغيرة متكررة تتحول إلى أسطر قليلة يمكن الوثوق بها.

كيف ينفّذ الحاسوب ما تكتبه؟ تكتب الكود في ملف نصي امتداده .py، ثم يقرؤه مفسّر بايثون (Interpreter) من الأعلى إلى الأسفل وينفّذه سطراً بعد سطر. إذا صادف خطأً في سطر ما توقف عنده وعرض رسالة تشرح المشكلة، ولا تُنفَّذ الأسطر التي تليه. لهذا الترتيب نتيجة عملية: السطر الخامس لا يعرف شيئاً عن متغير ستعرّفه في السطر العاشر.

أول دالة تحتاجها هي print، وهي تعرض ما بين قوسيها على الشاشة ثم تنتقل إلى سطر جديد. النص يوضع بين علامتي اقتباس، أما العمليات الحسابية مثل 120 + 85 فتُكتب بلا اقتباس فيُحسب ناتجها قبل الطباعة. إذا فصلت بين عدة قيم بفاصلة طبعتها print في سطر واحد تفصل بينها مسافة، والوسيط sep يغيّر هذه المسافة إلى أي فاصل تريده. وحتى النص يقبل الضرب في عدد: "=" * 24 يطبع خطاً من أربع وعشرين علامة. والسطر الذي يبدأ بعلامة # تعليق (Comment) يتجاهله المفسّر، ويُكتب لشرح سبب وجود الكود، أما ما يفعله فيُفترض أن يتضح من الكود نفسه.

سجل أخطاء اليوم الأول — ثلاثة أخطاء يقع فيها كل مبتدئ تقريباً، وما تعرضه بايثون، وطريقة الإصلاح:
1) كتابة Print("مرحبا") بحرف P كبير تعطي NameError: name 'Print' is not defined. بايثون تفرّق بين الأحرف الكبيرة والصغيرة، والاسم الصحيح print.
2) كتابة print("مرحبا) مع نسيان علامة الإغلاق تعطي SyntaxError تشير إلى السطر نفسه. أغلق النص بالعلامة ذاتها التي فتحته بها.
3) كتابة print "مرحبا" بلا أقواس، كما في أمثلة قديمة مكتوبة لبايثون 2، تعطي SyntaxError أيضاً. في بايثون 3 الأقواس إلزامية لأن print دالة.

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

المحرر في هذه الصفحة يشغّل الكود داخل المتصفح بمكتبة Skulpt، وهي تنفيذ للغة بايثون مكتوب بلغة JavaScript، فلا تحتاج إلى تثبيت أي برنامج لتبدأ. يدعم المحرر الأساسيات التي يغطيها المسار، لكنه ليس بايثون الكاملة: بعض الوحدات غير موجودة فيه، وقد تختلف صياغة بعض رسائل الخطأ عمّا تراه على جهازك. عندما تنتقل إلى جهازك نزّل بايثون 3 من صفحة التنزيلات الرسمية في python.org؛ فبايثون 2 توقّف دعمها في الأول من يناير 2020 ولا يصح البدء بها.

عادة مفيدة من اليوم الأول: قبل الضغط على «تشغيل» اكتب على ورقة ما تتوقع أن يظهر. إذا طابق الناتج توقعك فقد فهمت السطر، وإذا اختلف فقد وجدت سؤالاً يستحق البحث، وهذه أسرع طريقة لبناء فهم حقيقي بدل النسخ واللصق.

  • print("مرحبا")
  • print("المجموع:", 120 + 85)
  • print("=" * 24)
  • print("أ", "ب", sep=" | ")
  • # تعليق يتجاهله المفسّر

جرّب بنفسك

الناتج يظهر هنا…

مهمة: غيّر الرسالة الأولى لتطبع اسمك، وأضف سطراً يطبع مجموع ثلاثة مبالغ من اختيارك. احذف علامة # من السطر الأخير وتوقّع الناتج قبل التشغيل، ثم اكتب Print بحرف كبير عمداً واقرأ رسالة الخطأ من سطرها الأخير.

اختبر فهمك (1/3)

أي سطر يطبع كلمة مرحبا في بايثون 3 دون خطأ؟

اختبر فهمك (2/3)

ظهرت رسالة خطأ من عدة أسطر عند التشغيل. من أين تبدأ قراءتها؟

اختبر فهمك (3/3)

ماذا يفعل المفسّر بالسطر الذي يبدأ بعلامة #؟

للاستزادة: استخدام مفسّر بايثون — الدليل الرسمي · مقدمة غير رسمية إلى بايثون · Skulpt — تنفيذ بايثون داخل المتصفح

2. المتغيرات والأنواع

الهدف: تخزّن القيم في أسماء واضحة، وتختار النوع المناسب لكل بيان، وتحوّل بين الأنواع دون أن تفقد معلومات.

المتغير (Variable) اسم يشير إلى قيمة في الذاكرة. تكتب الاسم ثم علامة = ثم القيمة: students = 28. العلامة هنا تعني «أسنِد»، أما سؤال التساوي فله علامة مزدوجة == ستراها في درس الشروط. لذلك يصح أن تكتب students = students + 2؛ يُحسب الطرف الأيمن أولاً بالقيمة القديمة، ثم يُربط الاسم بالناتج الجديد. ولا تحتاج إلى إعلان النوع مسبقاً، فبايثون تعرفه من القيمة نفسها لحظة الإسناد.

الأنواع الأربعة التي ستقابلها كل يوم:
• str نص بين علامتي اقتباس، مثل "طنطا" أو "01001234567"
• int عدد صحيح بلا كسر، مثل 28 أو -5
• float عدد عشري، مثل 31.5
• bool قيمة منطقية: True أو False بحرف أول كبير
والدالة type() تكشف نوع أي قيمة، فيظهر الناتج بالشكل <class 'int'>.

قبل وبعد: أسماء تشرح نفسها. كتب طالب برنامجاً صغيراً لمحل عصائر في الإسكندرية بهذه الأسماء: a = 15 وb = 40 وc = a * b. بعد أسبوع لم يتذكر هل a سعر الكوب أم عدد الأكواب. أعاد كتابة الأسطر نفسها هكذا: cup_price = 15 وcups_sold = 40 وrevenue = cup_price * cups_sold. الناتج لم يتغير، لكن السطر الثالث أصبح جملة مفهومة لأي قارئ. والعرف الذي يوصي به دليل الأسلوب PEP 8 أن تُكتب أسماء المتغيرات بحروف صغيرة وتُفصل كلماتها بشرطة سفلية.

وللتسمية قواعد تفرضها اللغة نفسها: يبدأ الاسم بحرف أو شرطة سفلية ولا يبدأ برقم، ولا يحتوي مسافة أو شرطة عادية، ولا يكون كلمة محجوزة مثل if أو for أو class. والاسم Total يختلف عن total. تقبل بايثون 3 أسماء مكتوبة بحروف عربية، لكن العرف السائد في المشاريع المشتركة هو الأسماء الإنجليزية، لأنها أسهل في الكتابة والبحث وتبادل الكود مع الآخرين.

كيف تختار النوع؟ اسأل نفسك: هل سأجري على هذه القيمة عملية حسابية؟ عمر الطالب وعدد القطع يُجمعان ويُطرحان، فهما int. درجة الحرارة والوزن فيهما كسور، فهما float. أما رقم الهاتف والرقم القومي والرمز البريدي فلا أحد يجمعها، وقد تبدأ بصفر له معنى، لذلك تُحفظ نصاً. جرّب int("01001234567") وستحصل على 1001234567: ضاع الصفر الأول وتغيّر الرقم دون أي رسالة خطأ، وهذا أخطر أنواع الأخطاء لأنه صامت.

التحويل بين الأنواع يكون صريحاً دائماً. str(28) تعطي النص "28"، وint("28") تعطي العدد 28، وfloat("2.5") تعطي 2.5. وإذا حاولت لصق نص بعدد مثل "العدد: " + 28 توقّف البرنامج بخطأ TypeError، لأن بايثون لا تخمّن هل تريد جمعاً حسابياً أم لصقاً نصياً. والعكس يفاجئ المبتدئين أيضاً: "5" + "5" تعطي "55" لأن القيمتين نصّان، بينما 5 + 5 تعطي 10.

اختبار فهم قبل التشغيل: نفّذ x = 5 ثم y = x ثم x = 9، ثم اطبع y. الناتج 5، لأن y = x جعلت y يشير إلى القيمة 5 لحظة التنفيذ، وإعادة ربط x بقيمة جديدة لا تغيّر ما يشير إليه y. اكتب توقعك على ورقة أولاً، ثم قارنه بآخر سطر في المحرر. ستعود إلى هذه الفكرة في درس القوائم، حيث تتصرف القيم القابلة للتعديل بطريقة تستحق الانتباه.

اختصاران ستراهما كثيراً: students += 2 تعادل students = students + 2، ومثلها -= و*=. وتستطيع إسناد عدة متغيرات في سطر واحد: width, height = 8, 5، بشرط أن يتساوى عدد الأسماء مع عدد القيم، وإلا توقف البرنامج بخطأ ValueError.

  • cup_price = 15
  • type(31.5) # <class 'float'>
  • int("0100") # 100
  • str(28) + " طالباً"
  • x = x + 1

جرّب بنفسك

الناتج يظهر هنا…

مهمة: أنشئ متغيرات لمنتج في محل تعرفه: الاسم (str)، والسعر (float)، والكمية (int)، وهل هو متوفر (bool). اطبع نوع كل متغير بـ type()، ثم اطبع جملة «سعر ... هو ...» مرة باستخدام str() ومرة بالفاصلة داخل print.

اختبر فهمك (1/3)

ما نوع القيمة "120" المكتوبة بين علامتي اقتباس؟

اختبر فهمك (2/3)

أي طريقة أنسب لحفظ رقم هاتف مثل 01001234567؟

اختبر فهمك (3/3)

بعد تنفيذ x = 5 ثم y = x ثم x = 9، ما قيمة y؟

للاستزادة: الأعداد والنصوص في الدليل الرسمي · الأنواع المضمّنة في بايثون · PEP 8 — قواعد التسمية

3. الإدخال والحساب

الهدف: تحوّل ما يكتبه المستخدم إلى أعداد، وتستخدم عوامل الحساب السبعة وترتيب تنفيذها، وتعرض النتائج بتنسيق مرتب.

الدالة input() توقف البرنامج حتى يكتب المستخدم سطراً ويضغط Enter، ثم تعيد ما كتبه نصاً دائماً، حتى لو كتب أرقاماً فقط. هذا ما ينص عليه التوثيق الرسمي: تقرأ سطراً من الإدخال وتحوّله إلى نص. لذلك تحوّل الإدخال قبل أي حساب: int() للأعداد الصحيحة وfloat() للعشرية. في محرر هذه الصفحة نكتب القيم داخل الكود مباشرة بدل input() حتى يعمل المثال بضغطة واحدة، وعلى جهازك تستبدل السطر بالنسخة التي تقرأ من لوحة المفاتيح، وهي مكتوبة في تعليق أعلى المثال.

بناء خطوة بخطوة: أربعة أصدقاء في مقهى، والفاتورة 860 جنيهاً، ويريدون إضافة بقشيش بنسبة 10٪ ثم تقسيم المبلغ بالتساوي.
1) القيم: bill = 860 وpeople = 4 وtip_percent = 10. على جهازك تأتي الأولى من int(input("قيمة الفاتورة: ")).
2) البقشيش: tip = bill * tip_percent / 100 يعطي 86.0. علامة / تعطي عدداً عشرياً دائماً، حتى لو كانت القسمة بلا باقٍ.
3) الإجمالي والنصيب: total = bill + tip يساوي 946.0، وshare = total / people يساوي 236.5.
4) الأوراق النقدية: إذا دفع كل شخص بأوراق فئة 50، فإن 236 // 50 تعطي 4 ورقات، و236 % 50 تعطي 36 جنيهاً تُدفع بفئات أصغر.

لاحظ أننا كتبنا النسبة عدداً صحيحاً ثم قسمنا على 100، ولم نكتب bill * 0.1. الأعداد العشرية تُخزَّن في الحاسوب بتمثيل ثنائي تقريبي، فبعض الكسور مثل 0.1 لا تُحفظ بدقة تامة، وقد تظهر أرقام غريبة في المنزلة السادسة عشرة. يشرح الدليل الرسمي هذه الظاهرة في فصل كامل عن حساب الفاصلة العائمة، والقاعدة العملية للمبالغ المالية: احسب بأعداد صحيحة كلما أمكن، وقرّب عند العرض فقط.

العوامل السبعة: + و- و* للجمع والطرح والضرب. / قسمة عادية: 10 / 2 تعطي 5.0. // قسمة صحيحة تقرّب الناتج إلى الأسفل: 17 // 5 تعطي 3. % باقي القسمة: 17 % 5 تعطي 2، ويُستعمل كثيراً لمعرفة الزوجي، فالباقي صفر عند القسمة على 2. وعامل الأس يُكتب بنجمتين متتاليتين، فرفع 2 إلى القوة 10 يعطي 1024، وتجد صيغته في المثال والملخص أسفل الدرس. و«التقريب إلى الأسفل» في // يعني نحو سالب ما لا نهاية، فناتج -7 // 2 هو -4 وليس -3.

ترتيب التنفيذ كما في الرياضيات: الأقواس أولاً، ثم الأس، ثم الضرب والقسمة بأنواعها والباقي، ثم الجمع والطرح. لذلك 2 + 3 * 4 تساوي 14، بينما (2 + 3) * 4 تساوي 20. عندما تشك في الترتيب أضف أقواساً؛ فهي لا تكلّف شيئاً وتوضّح نيتك لمن يقرأ الكود بعدك.

تنسيق الناتج: السلسلة النصية المنسّقة (f-string) تبدأ بالحرف f قبل علامة الاقتباس، وتضع داخلها أي تعبير بين قوسين معقوفين: f"النصيب: {share:.2f}" تطبع النصيب: 236.50. الجزء :.2f يعني منزلتين عشريتين بالضبط. أما round(x, 2) فتعيد عدداً مقرّباً يصلح لحساب لاحق، وتتبع قاعدة التقريب إلى الزوجي عند التعادل التام: round(2.5) تعطي 2، وround(3.5) تعطي 4. هذا السلوك موثّق رسمياً، ويفاجئ من يتوقع التقريب المدرسي.

خطأ متكرر: كتابة total = input("السعر: ") * 3 دون تحويل. إذا كتب المستخدم 250 فالناتج هو النص 250250250 بدل 750، لأن ضرب النص في عدد يكرره. وإذا كتب حروفاً ثم حوّلتها بـ int() ظهر ValueError. الحل العملي: حوّل في سطر مستقل باسم واضح مثل price = int(price_text)، فإن ظهر خطأ عرفت مصدره فوراً. وفي درس القواميس والأخطاء ستتعلم كيف تلتقط هذا الخطأ وتعرض رسالة مفهومة بدل توقف البرنامج.

  • int(input("العدد: "))
  • 17 // 5 # 3 17 % 5 # 2
  • 2 ** 10 # 1024
  • f"{share:.2f}"
  • round(2.5) # 2

جرّب بنفسك

الناتج يظهر هنا…

مهمة: مستطيل طوله 8 وعرضه 5: اطبع مساحته ومحيطه بجملتين عربيتين باستخدام f-string. ثم احسب كم صندوقاً سعته 12 قطعة يلزم لتعبئة 100 قطعة: استخدم // لعدد الصناديق الممتلئة و% لعدد القطع المتبقية، وتحقق أن الناتجين 8 و4.

اختبر فهمك (1/3)

ما ناتج 7 // 2 في بايثون؟

اختبر فهمك (2/3)

كتب المستخدم 250 في input()، ثم ضرب البرنامج النص المُدخل في 3 دون تحويل. ما الناتج؟

اختبر فهمك (3/3)

ماذا تطبع print(round(2.5)) في بايثون 3؟

للاستزادة: الدوال المضمّنة: input وint وround · الإدخال والإخراج وتنسيق f-string · حساب الفاصلة العائمة: المشكلات والحدود

4. اتخاذ القرار: if / elif / else

الهدف: تحوّل قاعدة عمل مكتوبة إلى سلسلة شروط صحيحة الترتيب، وتختبر حدودها، وتتجنب أخطاء المقارنة والإزاحة.

الشرط يجعل البرنامج يختار بين مسارات. if تفحص تعبيراً نتيجته True أو False، فإن كان صحيحاً نُفّذت الأسطر المزاحة تحتها. elif (اختصار else if) تفحص شرطاً آخر إن فشل ما قبله، وelse تلتقط كل ما تبقى. وتُنفَّذ كتلة واحدة فقط من السلسلة كلها: أول شرط صحيح يفوز، ثم تقفز بايثون إلى ما بعد السلسلة دون فحص الباقي.

بعد النقطتين : تأتي كتلة مزاحة، والإزاحة جزء من قواعد اللغة نفسها، فهي التي تحدد أين تبدأ الكتلة وأين تنتهي. دليل الأسلوب PEP 8 يوصي بأربع مسافات لكل مستوى. وخلط المسافات مع مفتاح Tab في الكتلة نفسها يسبب TabError أو IndentationError، لذلك اضبط محررك ليحوّل Tab إلى أربع مسافات تلقائياً.

من سياسة مكتوبة إلى كود. متجر إلكتروني صغير في القاهرة يبيع مستلزمات الخياطة، وصاحبته كتبت سياسة الشحن في ورقة: الطلب الذي قيمته 1000 جنيه فأكثر شحنه مجاني. الطلب من 500 إلى أقل من 1000 شحنه 30 جنيهاً. الطلب الأقل من 500 شحنه 60 جنيهاً. وأي قيمة صفر أو سالبة خطأ في الإدخال. الأرقام هنا افتراضية للتدريب، والمهم طريقة التحويل.

قبل الكتابة رتّب القواعد كجدول من الأعلى إلى الأدنى: حالة الخطأ أولاً، ثم 1000 فأكثر، ثم 500 فأكثر، ثم الباقي. لماذا هذا الترتيب؟ لو بدأت بالشرط total >= 500 لدخل فيه طلب قيمته 1500 أيضاً، ودفع العميل 30 جنيهاً رغم أنه يستحق الشحن المجاني، لأن أول شرط صحيح هو الذي يُنفَّذ وحده. القاعدة عند الشروط المتداخلة: ابدأ بالحالة الأضيق أو بالحد الأعلى، ثم انزل تدريجياً.

قائمة فحص الحدود: كل رقم في السياسة المكتوبة حدٌّ يجب اختباره صراحة. للسياسة السابقة جرّب القيم التالية:
• 0 و-50: يجب أن تظهر رسالة خطأ.
• 499 و500: الأولى شحنها 60 والثانية 30.
• 999 و1000: الأولى شحنها 30 والثانية مجاني.
• قيمة كبيرة مثل 4000: شحن مجاني.
معظم أخطاء الشروط تختبئ عند الحد نفسه، حين تكتب > وأنت تقصد >=.

عوامل المقارنة ستة: == يساوي، و!= لا يساوي، ثم < و> و<= و>=. وتسمح بايثون بالمقارنة المتسلسلة، فتكتب 500 <= total < 1000 كما تكتبها في الرياضيات. ولدمج الشروط: and تحتاج صحة الطرفين، وor يكفيها أحدهما، وnot تعكس القيمة. وبايثون تتوقف عن التقييم مبكراً متى عرفت النتيجة: في x != 0 and 10 / x > 2 لا تُحسب القسمة إطلاقاً إذا كان x صفراً، فلا يقع خطأ القسمة على صفر.

أخطاء شائعة تستحق الحفظ: كتابة if total = 1000: بعلامة إسناد واحدة تعطي SyntaxError، والمقصود ==. ونسيان النقطتين في آخر سطر if يعطي الخطأ نفسه. وانتبه إلى أن القيم «الفارغة» تُعامل كأنها False: الصفر والنص الفارغ "" والقائمة الفارغة. لذلك يكفي if name: لتعرف أن المستخدم كتب اسماً، لكن هذا الاختصار يخدعك مع الأعداد إذا كان الصفر قيمة مقبولة في برنامجك، فاكتب المقارنة صراحة عندها.

وعندما يطول الشرط خزّن أجزاءه في متغيرات بأسماء واضحة قبل if، مثل is_big_order = total >= 1000، ثم اكتب if is_big_order or is_member:. يصبح الشرط جملة تُقرأ مباشرة، ويسهل طباعة كل جزء وحده عند تتبّع خطأ في النتيجة.

في آخر المثال حلقة تعيد فحص القيم الحدّية دفعة واحدة. لا تقلق من صياغتها الآن، فستتعلم حلقة for في الدرس التالي؛ المهم أن تقارن كل سطر من ناتجها بقائمة الفحص أعلاه.

  • if total >= 1000:
  • elif total >= 500:
  • else:
  • 500 <= total < 1000
  • x != 0 and 10 / x > 2

جرّب بنفسك

الناتج يظهر هنا…

مهمة: أضف قاعدة جديدة: الطلب من 2000 فأكثر يحصل على شحن مجاني وخصم 5٪. ضعها في المكان الصحيح من السلسلة، وأضف 1999 و2000 إلى قائمة الاختبار، وتحقق أن كل قيمة تقع في فرع واحد فقط.

اختبر فهمك (1/3)

في سلسلة if/elif تحقق شرطان صحيحان معاً. أي كتلة تُنفَّذ؟

اختبر فهمك (2/3)

ماذا يحدث عند تشغيل سطر مكتوب هكذا: if total = 1000:

اختبر فهمك (3/3)

ما قيمة التعبير 500 <= 1000 < 1000؟

للاستزادة: أدوات التحكم في التدفق: if وغيرها · اختبار القيمة المنطقية وعوامل المقارنة · PEP 8 — الإزاحة بأربع مسافات

5. التكرار: for و while

الهدف: تكرر العمل دون نسخ الأسطر، وتختار بين for وwhile، وتكتشف أخطاء الحلقات الثلاثة الأشهر وتصلحها.

التكرار (Loop) ينفّذ كتلة الكود نفسها عدة مرات. في بايثون أداتان: حلقة for التي تمر على عناصر مجموعة معروفة واحداً تلو الآخر، وحلقة while التي تكرر ما دام شرطها صحيحاً. القاعدة العملية للاختيار: إن كنت تعرف ما ستمر عليه، قائمةً كان أو نصاً أو مدى من الأرقام، فاستخدم for. وإن كان التوقف مرتبطاً بحدث لا تعرف متى يقع، كوصول رصيد إلى هدف أو إدخال صحيح من المستخدم، فاستخدم while.

أكثر ما يُستخدم مع for الدالة range. range(5) تولّد 0 و1 و2 و3 و4: تبدأ من الصفر وتتوقف قبل الرقم المكتوب. وrange(1, 6) من 1 إلى 5. والوسيط الثالث خطوة: range(0, 20, 5) تعطي 0 و5 و10 و15، وrange(10, 0, -3) تعدّ تنازلياً 10 و7 و4 و1. وحلقة for تمر على حروف النص أيضاً، فـ for ch in "سلام" تعطيك الحروف الأربعة واحداً بعد الآخر.

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

العطل الأول: الرقم الأخير مفقود. أراد طالب طباعة الأعداد من 1 إلى 10، فكتب:

for n in range(1, 10):
    print(n)

ظهرت الأعداد حتى 9 فقط، لأن range لا تشمل حدها الأعلى. الإصلاح: range(1, 11). يسمى هذا النوع خطأ الواحد الزائد أو الناقص (Off-by-one)، وهو من أكثر أخطاء البرمجة شيوعاً في كل اللغات.

العطل الثاني: المجموع خاطئ دائماً. أرادت طالبة جمع مبيعات ثلاثة أيام، فكتبت:

for sale in [120, 80, 200]:
    total = 0
    total = total + sale

كانت النتيجة 200، أي آخر رقم فقط. السطر total = 0 داخل الحلقة يصفّر المجموع في كل دورة. الإصلاح: انقل التهيئة إلى ما قبل الحلقة. نمط المُجمِّع (Accumulator) ثابت لا يتغير: تهيئة قبل الحلقة، وتحديث داخلها، واستخدام النتيجة بعدها.

العطل الثالث: البرنامج لا يتوقف. كتب متدرب حلقة تحسب عدد الأشهر اللازمة لادخار 3000 جنيه بمعدل 400 شهرياً:

saved = 0
months = 0
while saved < 3000:
    months = months + 1

نسي سطر saved = saved + 400، فبقي الشرط صحيحاً إلى الأبد. محرر الصفحة يوقف التنفيذ بعد بضع ثوانٍ ويعرض خطأ تجاوز المهلة، أما على جهازك فيستمر البرنامج حتى تضغط Ctrl+C. قبل كتابة أي while اسأل: ما السطر الذي يقرّب الشرط من نهايته في كل دورة؟

حلقة while بحد أقصى: عند طلب كلمة مرور من المستخدم لا تريد حلقة بلا نهاية إذا أخطأ مراراً. الحل الشائع شرط مركّب مثل while attempts < 3 and not ok:، فتنتهي الحلقة بأحد سببين: نجاح الإدخال أو استنفاد المحاولات. بعد الحلقة افحص ok لتعرف أي السببين حدث، واعرض رسالة مناسبة لكل حالة. يتكرر هذا النمط في كل برنامج يتعامل مع إدخال بشري أو اتصال شبكي قد يفشل.

أدوات إضافية تحتاجها كثيراً: break تخرج من الحلقة فوراً، وcontinue تتخطى بقية الدورة الحالية إلى التي تليها، والدالة enumerate تعطيك رقم العنصر والعنصر معاً، كما في for i, day in enumerate(days, start=1)، فلا تحتاج إلى عدّاد يدوي. وإذا لم تفهم سلوك حلقة فتتبّعها يدوياً في جدول على ورقة: عمود لكل متغير وسطر لكل دورة، أو أضف print مؤقتاً داخلها ثم احذفه بعد أن تفهم ما يحدث. هذه الطريقة البسيطة تكشف معظم أخطاء الحلقات، ويستعملها المحترفون أيضاً.

  • for i in range(1, 11): # 1..10
  • total = 0 # قبل الحلقة
  • while saved < 3000:
  • for i, x in enumerate(items, start=1):
  • break · continue

جرّب بنفسك

الناتج يظهر هنا…

مهمة: احسب بحلقة for مجموع الأعداد من 1 إلى 100 وتحقق أن الناتج 5050. ثم عدّل حلقة الادخار لتبدأ بمبلغ 500 مدّخر مسبقاً، وتوقّع عدد الأشهر على ورقة قبل التشغيل، ثم قارن توقعك بالناتج.

اختبر فهمك (1/3)

كم مرة تُنفَّذ كتلة الحلقة for n in range(1, 10)؟

اختبر فهمك (2/3)

كانت نتيجة المُجمِّع تساوي آخر عنصر في القائمة فقط. ما السبب الأرجح؟

اختبر فهمك (3/3)

ما الذي يمنع حلقة while من التكرار إلى الأبد؟

للاستزادة: for وrange وbreak وcontinue في الدليل الرسمي · الدوال المضمّنة: range وenumerate · المرجع اللغوي: جملتا while وfor

6. القوائم Lists

الهدف: تخزّن مجموعة قيم مرتبة في قائمة، وتصل إلى عناصرها وتقطعها وتعدّلها، وتفهم الفرق بين نسخ القائمة ومشاركتها.

القائمة (List) تحفظ عدة قيم مرتبة في متغير واحد، وتُكتب بين قوسين مربعين: scores = [78, 92, 65, 88]. بدل أربعة متغيرات منفصلة لأربع درجات تكفي قائمة واحدة تمر عليها بحلقة، ويبقى الكود نفسه صالحاً لو صارت الدرجات أربعين أو أربعمئة.

الفهرس (Index) يبدأ من صفر: scores[0] هي 78، وscores[3] هي 88. والفهرس السالب يعدّ من النهاية، فـ scores[-1] آخر عنصر مهما كان طول القائمة. والتقطيع (Slicing) يعطي قائمة جديدة: scores[1:3] من الفهرس 1 حتى ما قبل 3، وscores[:2] أول عنصرين، وscores[::-1] القائمة معكوسة. وطلب فهرس غير موجود مثل scores[4] في قائمة من أربعة عناصر يعطي IndexError، لأن آخر فهرس صالح هو الطول ناقص واحد.

حالة عملية: معلّمة رياضيات في الرياض تحفظ درجات اختبار قصير لطلاب فصلها في قائمة، وتريد ثلاثة أشياء: إضافة درجة طالب تأخر، وتصحيح درجة أُدخلت خطأً، ومعرفة أعلى درجة وأدناها والمتوسط. الأولى بـ scores.append(72)، والثانية بالإسناد إلى الفهرس scores[2] = 70 لأن القوائم قابلة للتعديل، والثالثة بالدوال الجاهزة max(scores) وmin(scores) وsum(scores) / len(scores). وإذا أرادت حذف درجة بقيمتها فإن scores.remove(88) تحذف أول عنصر يساويها، أما scores.pop() فتحذف العنصر الأخير وتعيده إليها.

لترتيب القائمة طريقتان بنتيجتين مختلفتين. sorted(scores) تعيد قائمة جديدة مرتبة وتترك الأصلية كما هي، وهذا مناسب حين تحتاج الترتيب الأصلي لاحقاً، كترتيب الطلاب في كشف الحضور. أما scores.sort() فترتب القائمة نفسها في مكانها وتعيد None؛ لذلك السطر scores = scores.sort() يمحو القائمة ويجعل المتغير None، وهو الخطأ نفسه الذي يحدث عند كتابة scores = scores.append(72). وللترتيب التنازلي أضف reverse=True إلى أي منهما.

مشاركة أم نسخ؟ في درس المتغيرات رأيت أن y = x ثم إعادة ربط x لا تؤثر في y. مع القوائم الصورة أدق: backup = scores تجعل الاسمين يشيران إلى القائمة نفسها في الذاكرة دون إنشاء قائمة ثانية. فإذا نفّذت scores.append(100) ظهرت الدرجة الجديدة في backup أيضاً، لأننا عدّلنا القائمة ولم نُعِد ربط الاسم. هذا مصدر أخطاء محيّرة حين تظن أنك احتفظت بنسخة احتياطية قبل التعديل. إن أردت نسخة مستقلة فاكتب backup = scores.copy() أو backup = scores[:].

سؤال العضوية 92 in scores يعطي True أو False، ويغنيك عن حلقة بحث كاملة. وصيغة القائمة المختصرة (List comprehension) تبني قائمة جديدة في سطر واحد: [s + 5 for s in scores] تضيف خمس درجات لكل طالب، و[s for s in scores if s >= 75] تبقي من حصل على 75 فأكثر. استخدمها للتحويلات البسيطة، وارجع إلى الحلقة العادية إذا طال السطر وصعبت قراءته.

والقائمة قد تحتوي قوائم أخرى: table = [[1, 2], [3, 4]]، فتصل إلى العدد 4 بـ table[1][1]؛ الفهرس الأول يختار الصف والثاني يختار العنصر داخله. هذا الشكل يمثّل الجداول البسيطة، مثل درجات عدة طلاب في عدة مواد.

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

  • scores[0] scores[-1] scores[1:3]
  • scores.append(72)
  • sorted(scores) # نسخة جديدة
  • safe = scores.copy()
  • [s for s in scores if s >= 75]

جرّب بنفسك

الناتج يظهر هنا…

مهمة: أنشئ قائمة بأسعار خمسة منتجات، واطبع أغلاها وأرخصها ومتوسطها. ثم أنشئ بالقائمة المختصرة قائمة جديدة بالأسعار بعد خصم 10٪، وتأكد بالطباعة أن القائمة الأصلية لم تتغير.

اختبر فهمك (1/3)

ما فهرس آخر عنصر في قائمة طولها 4؟

اختبر فهمك (2/3)

نفّذت backup = scores ثم scores.append(100). ماذا تحتوي backup الآن؟

اختبر فهمك (3/3)

أي سطر يعطيك نسخة مرتبة ويترك القائمة scores دون تغيير؟

للاستزادة: المزيد عن القوائم — الدليل الرسمي · دليل الترتيب: sorted وsort · القوائم والتقطيع في المقدمة الرسمية

7. الدوال Functions

الهدف: تغلّف منطقاً مكرراً في دالة لها معاملات وقيمة مرجعة، وتفرّق بين return وprint، وتفهم نطاق المتغيرات.

الدالة (Function) كتلة كود لها اسم، تكتبها مرة وتستدعيها كلما احتجتها. تعرّفها بالكلمة def ثم الاسم ثم قوسين فيهما المعاملات (Parameters) ثم نقطتين، وتحت ذلك جسم الدالة مزاحاً. وعند الاستدعاء تمرّر قيماً حقيقية تسمى الوسائط (Arguments)، مثل with_vat(200).

نسختان من البرنامج نفسه. محل إلكترونيات صغير في جدة يحسب سعر ثلاثة منتجات بعد إضافة ضريبة نفترض أنها 15٪ لأغراض التمرين. النسخة الأولى نسخت سطر الحساب ثلاث مرات: print(1200 + 1200 * 15 / 100) ثم الشيء نفسه للسعرين 350 و80. النسخة الثانية عرّفت دالة واحدة with_vat(price) تعيد السعر بعد الضريبة، ثم استدعتها ثلاث مرات داخل حلقة.

الفرق يظهر يوم تتغير النسبة: في النسخة الأولى تبحث عن كل سطر وتعدّله، وقد تنسى واحداً فيخرج سعر خاطئ لعميل. في النسخة الثانية تعدّل رقماً واحداً داخل الدالة. والدالة أيضاً أسهل اختباراً: تستدعيها بقيمة تعرف ناتجها، مثل with_vat(100) التي يجب أن تعطي 115.0، فإن اختلف الناتج عرفت أن الخطأ داخلها هي، فتبحث في أسطر قليلة بدل البرنامج كله. القاعدة العملية: إذا كتبت المنطق نفسه مرتين، فالمرة الثالثة تستحق دالة.

return أم print؟ print تعرض قيمة على الشاشة ولا تعطيها لأحد، أما return فتعيد القيمة إلى مكان الاستدعاء لتخزنها في متغير أو تدخلها في حساب آخر. والدالة التي لا تحتوي return تعيد القيمة الخاصة None تلقائياً. فإذا كتبت دالة تطبع السعر بدل أن تعيده، ثم حاولت جمع ناتجها مع رقم آخر، ظهر TypeError لأنك تجمع None مع عدد. وreturn تنهي الدالة فوراً، فأي سطر بعدها في الكتلة نفسها لا يُنفَّذ.

وانتبه إلى ترتيب الملف: بايثون تنفّذ الأسطر من الأعلى، فاستدعاء دالة قبل سطر def الخاص بها يعطي NameError. ونسيان القوسين خطأ صامت آخر: with_vat وحدها تشير إلى الدالة نفسها دون أن تستدعيها، فتطبع print(with_vat) وصفاً للدالة بدل رقم.

في التعريف def with_vat(price, rate=15) يأخذ المعامل rate القيمة الافتراضية 15 إن لم تمرّر غيرها، فتكتب with_vat(200) أو with_vat(200, 5). ويمكنك تمرير الوسائط بأسمائها وبأي ترتيب: with_vat(rate=5, price=200)، وهذا يوضّح الاستدعاء حين تكثر المعاملات. والدالة تستطيع إعادة أكثر من قيمة مفصولة بفاصلة، فتستقبلها في متغيرين: low, high = price_range(prices).

تتبّع استدعاء واحد خطوة بخطوة — عند تنفيذ total = with_vat(200, 5) تحدث أربعة أشياء بالترتيب:
1) يُنشأ داخل الدالة متغيران محليان: price = 200 وrate = 5.
2) يُحسب 200 * 5 / 100 فيساوي 10.0، ثم يُضاف إلى السعر.
3) تعيد return القيمة 210.0 وتنتهي الدالة.
4) يُربط الاسم total بالقيمة 210.0 خارج الدالة، ويختفي المتغيران المحليان.

النطاق (Scope): المتغيرات التي تُنشأ داخل الدالة، مثل price وrate، تعيش داخلها فقط، وطباعتها من الخارج تعطي NameError. وإذا كان في البرنامج متغير خارجي بالاسم نفسه فالإسناد داخل الدالة ينشئ متغيراً محلياً جديداً ولا يغيّر الخارجي. هذا العزل مقصود: تتواصل الدالة مع بقية البرنامج عبر المعاملات والقيمة المرجعة فقط، فتبقى مفهومة وحدها. سمِّ الدالة بفعل يصف عملها، مثل calc_total أو is_valid_phone، واكتب في أول سطر منها نصاً توثيقياً (Docstring) بين ثلاث علامات اقتباس يشرح ما تعيده.

  • def with_vat(price, rate=15):
  • return price + price * rate / 100
  • with_vat(rate=5, price=200)
  • low, high = price_range(prices)
  • show_price(99) # بلا return تعيد None

جرّب بنفسك

الناتج يظهر هنا…

مهمة: اكتب دالة is_even(n) تعيد True إذا كان العدد زوجياً، ودالة average(nums) تعيد متوسط قائمة. اختبر كل دالة بثلاث قيم تعرف ناتجها مسبقاً، واطبع نتيجة المقارنة بـ == كما في آخر المثال.

اختبر فهمك (1/3)

ماذا تعيد دالة لا تحتوي على return؟

اختبر فهمك (2/3)

مع التعريف def with_vat(price, rate=15)، ما ناتج with_vat(200, 5)؟

اختبر فهمك (3/3)

أُسندت قيمة إلى متغير اسمه rate داخل دالة، ثم طُبع خارجها بعد استدعائها. ما النتيجة؟

للاستزادة: تعريف الدوال — الدليل الرسمي · القيم الافتراضية والوسائط بالاسم · PEP 257 — أعراف النص التوثيقي

8. القواميس ومعالجة الأخطاء

الهدف: تربط المفاتيح بالقيم في قاموس وتعدّ التكرارات به، وتلتقط الأخطاء المتوقعة بأنواعها دون أن تخفي الأخطاء الحقيقية.

القاموس (Dictionary) يربط كل مفتاح (Key) بقيمة (Value)، ويُكتب بين قوسين معقوفين: stock = {"أرز": 40, "سكر": 25}. الوصول يكون بكتابة المفتاح بين قوسين مربعين: stock["أرز"] تعطي 40. الإسناد إلى مفتاح موجود يعدّل قيمته، وإلى مفتاح جديد يضيفه، وdel stock["سكر"] تحذفه. ومنذ بايثون 3.7 يضمن القاموس حفظ ترتيب إدخال المفاتيح، فتمر عليها الحلقة بالترتيب الذي أضفتها به.

بقالة في الدار البيضاء: صاحبها يتابع المخزون في قاموس، المفتاح اسم الصنف والقيمة عدد الوحدات. عند البيع يكتب stock["أرز"] = stock["أرز"] - 3. لكن ماذا لو سأل عن صنف لم يُسجَّل بعد مثل العدس؟ stock["عدس"] توقف البرنامج بخطأ KeyError. البديل الآمن stock.get("عدس", 0): تعيد القيمة إن وُجد المفتاح، وإلا تعيد القيمة الاحتياطية التي حددتها. وللسؤال قبل الوصول اكتب "عدس" in stock، وللمرور على الأزواج كلها for item, qty in stock.items().

من أشهر استخدامات القاموس عدّ التكرارات. لديك قائمة بالأصناف المباعة اليوم، وتريد عدد مرات بيع كل صنف. تبدأ بقاموس فارغ counts = {}، ثم لكل صنف تنفّذ counts[item] = counts.get(item, 0) + 1. أول ظهور للصنف يبدأ من الصفر، وكل ظهور بعده يضيف واحداً. وفي النهاية تعطيك max(counts, key=counts.get) الصنف الأكثر مبيعاً، لأن الوسيط key يجعل المقارنة تتم على القيم المرتبطة بالمفاتيح.

الاستثناءات (Exceptions): بعض الأخطاء متوقعة ولا تدل على عيب في الكود: مستخدم يكتب «عشرة» بدل 10، أو كمية صفر في مقام قسمة. بدل أن يتوقف البرنامج كله، تضع السطر الخطر داخل try، وتكتب لكل نوع متوقع كتلة except باسمه. كتلة else تُنفَّذ فقط إذا مرّ try بلا خطأ، وكتلة finally تُنفَّذ في كل الأحوال وتناسب أعمال التنظيف كإغلاق ملف. وتستطيع أنت أيضاً رفع خطأ عند بيانات مرفوضة: raise ValueError("الكمية لا تكون سالبة").

قائمة مراجعة قبل كتابة try:
• حدّد السطر الذي قد يفشل فعلاً، وضعه وحده تقريباً داخل try بدل تغليف البرنامج كله.
• سمِّ النوع المتوقع: ValueError للتحويل الفاشل، وZeroDivisionError للقسمة على صفر، وKeyError للمفتاح المفقود.
• رتّب كتل except من الأخص إلى الأعم، فبايثون تنفّذ أول كتلة يطابق نوعها الخطأ وتتجاهل الباقي.
• اكتب رسالة تخبر المستخدم بما يفعله، مثل «اكتب الكمية بالأرقام».
• لا تكتب except: وحدها، ولا except Exception: pass.

البند الأخير أهمها. الكتلة العامة الصامتة تبتلع كل الأخطاء، ومنها أخطاء حقيقية في كودك مثل اسم متغير مكتوب خطأً، فيستمر البرنامج بنتائج غريبة وتقضي ساعات تبحث عن السبب. امسك ما تتوقعه فقط، واترك غير المتوقع يظهر برسالته كاملة لتصلحه. ولاحظ في المثال أننا نطبع رسائل من كتابتنا بدل نص الخطأ الأصلي، لأن صياغة رسائل بايثون قد تختلف بين الإصدارات وبين محرر الصفحة وجهازك.

قاموس أم قائمة؟ اختر القاموس حين تبحث عن القيمة باسمها: student["grade"] تشرح نفسها، بينما student[1] تجبر القارئ على تذكّر ترتيب العناصر. واختر القائمة حين يكون الترتيب هو المعنى، كطابور عملاء أو درجات مرتبة زمنياً. وتذكّر أن مفاتيح القاموس لا تتكرر: الإسناد إلى مفتاح موجود يستبدل قيمته القديمة بصمت، فإن أردت الاحتفاظ بأكثر من قيمة لمفتاح واحد فاجعل القيمة قائمة، مثل {"أحمد": [90, 85]}.

  • stock.get("عدس", 0)
  • counts[k] = counts.get(k, 0) + 1
  • for k, v in stock.items():
  • try / except ValueError / else / finally
  • raise ValueError("رسالة واضحة")

جرّب بنفسك

الناتج يظهر هنا…

مهمة: أنشئ قاموساً لكتاب فيه العنوان وعدد الصفحات وسنة النشر، واطبع أزواجه بحلقة items(). ثم استخدم نمط العدّ لحساب تكرار كل حرف في كلمة «سلسلة» (الناتج المتوقع: س 2 ول 2 وة 1)، وأضف try/except تحوّل النص «مئة» إلى int وتطبع رسالة واضحة عند الفشل.

اختبر فهمك (1/3)

ماذا تعيد stock.get("زيت", 0) إذا لم يكن المفتاح موجوداً؟

اختبر فهمك (2/3)

متى تُنفَّذ كتلة else المرتبطة بـ try؟

اختبر فهمك (3/3)

لماذا تُعدّ كتابة except Exception: pass ممارسة سيئة؟

للاستزادة: القواميس في الدليل الرسمي · الأخطاء والاستثناءات: try وexcept وraise · قائمة الاستثناءات المضمّنة

9. الوحدات والمكتبات

الهدف: تستورد أدوات جاهزة من المكتبة القياسية بالصيغة المناسبة، وتعرف متى تحتاج حزمة خارجية وكيف تثبّتها في بيئة معزولة.

الوحدة (Module) ملف بايثون فيه دوال وقيم جاهزة. وتأتي بايثون مع المكتبة القياسية (Standard Library): مجموعة كبيرة من الوحدات للرياضيات والأرقام العشوائية والتواريخ والملفات والشبكات، متاحة فور تثبيت بايثون بلا أي خطوة إضافية. هذا الدرس مكتوب في صورة أسئلة يطرحها المبتدئون عادة عند أول لقاء مع الوحدات.

ما الفرق بين صيغ الاستيراد؟ import math تستورد الوحدة كاملة وتستدعي منها بالنقطة: math.sqrt(49). وfrom math import sqrt تستورد اسماً واحداً فتكتبه مباشرة: sqrt(49). وimport random as rnd تعطي الوحدة اسماً مختصراً. الصيغة الأولى أوضح في الملفات الطويلة لأن القارئ يرى مصدر كل دالة عند استخدامها. ويوصي دليل PEP 8 بوضع أسطر الاستيراد في أعلى الملف، وبتجنب from math import * لأنها تملأ برنامجك بأسماء لا يُعرف مصدرها.

ما الوحدات التي أحتاجها أولاً؟ math للجذر التربيعي math.sqrt، والتقريب لأعلى ولأسفل math.ceil وmath.floor، والثابت math.pi. وrandom للأرقام العشوائية: random.randint(1, 6) تعطي عدداً صحيحاً من 1 إلى 6 شاملاً الطرفين، وrandom.choice تختار عنصراً من قائمة، وrandom.shuffle تخلط القائمة في مكانها. وdatetime للتواريخ، فـ date(2026, 10, 3).weekday() تعطي رقم اليوم في الأسبوع، حيث الاثنين صفر والأحد ستة. ودالة math.ceil تحل مسألة متكررة: كم صندوقاً يلزم لمئة قطعة إذا كان الصندوق يتسع لاثنتي عشرة؟ القسمة تعطي 8.33 تقريباً، والتقريب لأعلى يعطي 9، لأن القطع الأربع الأخيرة تحتاج صندوقاً كاملاً.

لماذا تتغير النتائج العشوائية مع كل تشغيل، وكيف أثبّتها؟ أرقام random شبه عشوائية، تولّدها خوارزمية انطلاقاً من قيمة بداية تسمى البذرة (Seed). إذا لم تحددها تُشتق من مصدر متغير فتختلف النتائج كل مرة. أما random.seed(7) فتجعل التسلسل نفسه يتكرر في كل تشغيل، وهذا مفيد عند اختبار برنامج أو مشاركة مثال مع زميل. وتنبّه وثائق بايثون صراحة إلى أن وحدة random لا تصلح لأغراض الأمان كتوليد كلمات المرور، وتحيل في ذلك إلى وحدة secrets.

ماذا لو لم أجد ما أحتاجه في المكتبة القياسية؟ تبحث في فهرس الحزم PyPI عن حزمة خارجية، وتثبّتها بأداة pip. والطريقة التي يشرحها دليل التغليف الرسمي أن تنشئ أولاً بيئة افتراضية (Virtual environment) خاصة بالمشروع، حتى لا تختلط حزم مشروع بآخر، ثم تثبّت داخلها:

python -m venv .venv
python -m pip install requests

وبين الأمرين تفعّل البيئة: على ويندوز .venv\Scripts\activate، وعلى لينكس وماك source .venv/bin/activate. محرر هذه الصفحة لا يدعم الحزم الخارجية، ويدعم جزءاً فقط من المكتبة القياسية.

كتبت import random فظهر AttributeError، لماذا؟ السبب الأشهر أنك سمّيت ملفك random.py. عند الاستيراد تبحث بايثون في مجلد السكربت نفسه قبل مجلدات المكتبة، كما يشرح فصل الوحدات في الدليل الرسمي، فتجد ملفك وتستورده بدل الوحدة الأصلية، ولا تجد فيه randint. الحل: أعد تسمية الملف إلى اسم مثل dice_game.py، واحذف مجلد __pycache__ إن ظهر بجانبه. والخطأ المجاور له: كتابة sqrt(49) بعد import math دون البادئة، فيظهر NameError.

كيف أعرف دوال وحدة لا أعرفها؟ لكل وحدة قياسية صفحة في docs.python.org تشرح دوالها بأمثلة. وعلى جهازك تعرض help(math) داخل جلسة بايثون التفاعلية قائمة الدوال وشرحها، وdir(math) أسماءها فقط. وقبل إضافة أي حزمة خارجية اسأل: هل توجد وحدة قياسية تكفي؟ فكل حزمة تضيفها تبعية تحتاج تحديثاً ومراجعة مستمرة.

  • import math
  • from datetime import date
  • random.seed(7)
  • python -m venv .venv
  • python -m pip install requests

جرّب بنفسك

الناتج يظهر هنا…

مهمة: استخدم math.pi لحساب محيط دائرة نصف قطرها 7 ومساحتها، واطبع الناتجين بمنزلتين عشريتين عبر f-string (المتوقع 43.98 و153.94). ثم حاكِ رمي نردين عشر مرات بحلقة، واعرض كم مرة كان المجموع 7، وثبّت البذرة لتحصل على النتيجة نفسها عند إعادة التشغيل.

اختبر فهمك (1/3)

ما الأمر المناسب لتثبيت حزمة خارجية داخل بيئة المشروع المفعّلة؟

اختبر فهمك (2/3)

سمّيت ملفك random.py ثم كتبت فيه import random. ما المتوقع؟

اختبر فهمك (3/3)

أي وحدة تحيل إليها وثائق بايثون لتوليد رموز سرية مثل كلمات المرور؟

للاستزادة: الوحدات ومسار البحث عنها — الدليل الرسمي · وحدة random وتحذير الاستخدام الأمني · البيئات الافتراضية والحزم · دليل تثبيت الحزم بأداة pip

10. مشروع مصغّر: حاسبة طلب استضافة

الهدف: تجمع الشروط والدوال والقواميس والحلقات ومعالجة الأخطاء في برنامج صغير متماسك، وتختبره بجدول حالات معروفة النتيجة.

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

قبل أول سطر حدّد المدخلات، وهي عدد المواقع وعدد الأشهر، والمخرجات، وهي اسم الخطة والتكلفة، ثم القواعد: موقع واحد خطة «انطلاقة»، ومن 2 إلى 3 خطة «أعمال»، وأكثر من ذلك «احترافي»، والاشتراك لاثني عشر شهراً فأكثر يحصل على خصم 20٪. كتابة القواعد بالعربية أولاً تكشف الثغرات مبكراً: ماذا لو كان عدد المواقع صفراً؟ وماذا لو كُتب عدد سالب من الأشهر؟ واحتفظ بكل نسخة تعمل قبل أن تبدأ المرحلة التالية، ولو بنسخ الملف باسم جديد، حتى تعود إليها إن تعقّد شيء؛ وحين تتعلم Git لاحقاً سيؤدي هذا الدور بشكل أنظم.

المرحلة الأولى: دالة اختيار الخطة. suggest_plan(sites) سلسلة شروط مرتبة من الأصغر إلى الأكبر، وكل return ينهي الدالة فوراً، لذلك لا نحتاج elif بعده. عيب المسودة الأولى أنها كانت تقبل الصفر والأعداد السالبة وتعيد لها «انطلاقة»، فأضفنا في أولها فحصاً يرفع ValueError برسالة واضحة بدل نتيجة مضللة.

المرحلة الثانية: الأسعار في قاموس. بدل كتابة السعر داخل الشروط، وضعناه في قاموس PRICES يربط اسم الخطة بسعرها الشهري. الدالة subscription_cost(plan, months) تقرأ السعر من القاموس، وتضربه في عدد الأشهر، وتطبّق الخصم. الفائدة أن تغيير سعر خطة صار تعديل رقم واحد في مكان معروف، دون لمس منطق الحساب. والاسم مكتوب بحروف كبيرة لأن العرف في PEP 8 أن تُكتب الثوابت هكذا، فيعرف القارئ أن القيمة لا يُفترض تغييرها أثناء التشغيل.

المرحلة الثالثة: جدول اختبار. بدل تجربة القيم يدوياً كل مرة، كتبنا قائمة من الأزواج، في كل زوج مدخل والنتيجة المتوقعة له، ثم حلقة تقارن الناتج الفعلي بالمتوقع. السطر status = "نجح" if actual == expected else "فشل" تعبير شرطي في سطر واحد يختار إحدى القيمتين. أهم ما في الجدول الحالات الحدّية: 1 و2 لأنهما على حد الخطة الأولى، و3 و4 على حد الثانية. وأضفنا حالة الصفر داخل try لنتأكد أن الرفض يحدث فعلاً.

ما الذي تعثّر أثناء البناء؟ في محاولة سابقة ظهرت تكلفة 12 شهراً لخطة «أعمال» مساوية 1440 بلا خصم. السبب أن الشرط كُتب months > 12 بدل months >= 12، فلم تحصل حالة الاثني عشر شهراً بالضبط على الخصم. كشف الخطأ سطر الاختبار الذي يطبع القيمة المتوقعة 1152.0 بجانب الناتج الفعلي. هذا ما تفعله الاختبارات: تحوّل الانطباع بأن البرنامج يعمل إلى نتيجة مطبوعة يمكن التحقق منها في ثانية، وتنبّهك فوراً إذا كسر تعديل جديد شيئاً كان يعمل.

بعد المسار ابنِ مشروعاً صغيراً كل أسبوع بالخطوات نفسها: قواعد مكتوبة، ثم نسخة صغيرة، ثم جدول اختبار. أفكار مناسبة: حاسبة معدل فصلي، أو قائمة مهام في قاموس، أو لعبة تخمين رقم باستخدام random. وللقراءة المكمّلة بالعربية راجع كتاب «بايثون للجميع» بترجمته العربية المذكورة في مكتبة المسار. وحين تكبر مشاريعك انتقل إلى وحدة unittest القياسية لكتابة الاختبارات بشكل منظم، ثم إلى فصل الأصناف (Classes) في الدليل الرسمي عندما لا تعود الدوال وحدها كافية لتنظيم البرنامج.

  • PRICES = {"أعمال": 120}
  • raise ValueError("...")
  • cases = [(1, "انطلاقة"), (4, "احترافي")]
  • status = "نجح" if a == b else "فشل"
  • if months >= 12:

جرّب بنفسك

الناتج يظهر هنا…

مهمة: أضف خطة رابعة «مؤسسات» بسعر 600 لمن لديه أكثر من 8 مواقع، وأضف إلى جدول الاختبار الحالتين 8 و9 بنتيجتيهما المتوقعتين. ثم أضف خصماً 10٪ للاشتراك من 6 إلى 11 شهراً، وتحقق أن تكلفة 6 أشهر لخطة «انطلاقة» أصبحت 270.0.

اختبر فهمك (1/3)

لماذا وُضعت الأسعار في قاموس PRICES بدل كتابتها داخل الشروط؟

اختبر فهمك (2/3)

أي مجموعة قيم أفضل لاختبار قاعدة «من 2 إلى 3 مواقع خطة أعمال»؟

اختبر فهمك (3/3)

كشف الاختبار أن اشتراك 12 شهراً لم يحصل على الخصم. ما الإصلاح الأرجح؟

للاستزادة: رفع الاستثناءات بـ raise · وحدة unittest للاختبارات المنظمة · الأصناف — الخطوة التالية في الدليل الرسمي

مراجع رسمية لهذا المسار

أحسنت — أنهيت المسار العربي التفاعلي

وسّع بالقراءة العربية المفتوحة، أو اطلب مشروعاً حقيقياً ببايثون على استضافة MesueTech.