اتخاذ القرارات — if و elif و else
سلوك مسارات مختلفة بناءً على الشروط، لماذا يُعد التنسيق والمسافات البادئة ذا معنى في بايثون، والخطأ الخفي الذي يحدث عند ترتيب الشروط بشكل خاطئ.
كيف تريد أن تتعلم؟
المشكلة التي نقوم بحلها
في الفصل السابق تعلّمنا طرح الأسئلة — هل age >= 18 صحيحة أم خاطئة. لكن البرنامج لم يفعل شيئاً بالجواب، بل كان يطبع True أو False فقط.
حان الوقت الآن للعمل بهذا الجواب. مسار إذا تحقق الشرط، ومسار آخر إذا لم يتحقق. هذه هي اللحظة التي يتوقف فيها الكود عن كونه قائمة خطوات ويبدأ في اتخاذ القرارات.
وهنا يختلف بايثون عن كثير من لغات البرمجة الأخرى: الأسطر التي تقع داخل الشرط والأسطر التي تقع خارجه تتحدد بالمسافات البيضاء (whitespace). لا توجد أقواس معقوصة {}. يبدو هذا غريباً في البداية ثم تبدأ في الإعجاب به — لأن الكود الذي يبدو منظماً هو منظم فعلاً.
في نهاية هذا الفصل ستكون قادراً على
- تشغيل الكود بشرط باستخدام
if - توفير مسار بديل باستخدام
else، وعدة مسارات باستخدامelif - كتابة الإزاحة (indentation) بشكل صحيح، وقراءة
IndentationErrorوإصلاحه - شرح لماذا يهم ترتيب الشروط — وهنا أكبر فخ
- وضع شرط داخل شرط آخر، ومعرفة متى يجب تجنّب ذلك
المتطلبات السابقة: True و False — تعلّم طرح الأسئلة.
أبسط شكل
age = 22
if age >= 18:
print("You may enter")
print("Program finished")You may enter
Program finishedيمكن تقسيم الشكل إلى ثلاثة أجزاء:
- الكلمة
if، ثم شرط - نقطتان
:في نهاية الشرط — نسيانهما هو الخطأ الأكثر شيوعاً - الأسطر التالية مُزاحة بأربع مسافات — هذه الأسطر المُزاحة هي ما يحكمه الشرط
لو كانت age تساوي 15، لما نُفّذ أول print، لكن الأخير سيُنفَّذ رغم ذلك — لأنه خارج الشرط.
الإزاحة هي المعنى
هذان البرنامجان فيهما الأحرف نفسها تقريباً، لكن سلوكهما مختلف تماماً:
score = 30
if score >= 50:
print("Passed")
print("Certificate issued")شغّل هذا البرنامج ولن يظهر شيء في الطرفية — السطران كلاهما داخل الشرط، والشرط خاطئ.
score = 30
if score >= 50:
print("Passed")
print("Certificate issued")Certificate issuedأصبح print الثاني الآن خارج الشرط، لذلك يُنفَّذ دائماً — وقد صدرت للتو شهادة لشخص راسب. سطر واحد بإزاحة خاطئة يكسر قاعدة العمل، ولا يُظهر بايثون أي خطأ، لأن النسختين كلتيهما كود صحيح.
أربع مسافات، لا علامة جدولة (tab). بايثون يقبل الاثنين، لكن خلطهما في ملف واحد يعطي TabError، ومن الصعب العثور عليه لأن علامات الجدولة والمسافات تبدو متطابقة على الشاشة. في VS Code الذي ثبّته في الفصل 1، يؤدي الضغط على Tab في ملف بايثون إلى إدراج أربع مسافات — هذا هو إعداده الافتراضي، والمسافات الأربع هي العرف الرسمي في بايثون.else — المسار الآخر
age = 15
if age >= 18:
print("You may enter")
else:
print("Sorry, adults only")Sorry, adults onlyلا يحمل else أي شرط، لأن شرطه قد قيل مسبقاً — "إذا لم يتحقق أي مما سبق". يُنفَّذ واحد بالضبط من if و else؛ لا الاثنان معاً، ولا أي منهما يُترك دون الآخر.
elif — عدة مسارات
marks = 72
if marks >= 80:
grade = "A"
elif marks >= 70:
grade = "B"
elif marks >= 60:
grade = "C"
elif marks >= 40:
grade = "D"
else:
grade = "F"
print(f"Marks {marks} gives grade {grade}")Marks 72 gives grade Belif تعني "وإلا، إذا". ما يحدث هو التالي: يمرّ بايثون على السلسلة من الأعلى إلى الأسفل، وينفّذ جسم أول شرط يجده صحيحاً، ثم يغادر السلسلة كلها. ولا يفحص بقية الشروط أصلاً.
مع marks = 72: 72 >= 80 خاطئة، و 72 >= 70 صحيحة → grade = "B"، وانتهى. 72 >= 60 و 72 >= 40 صحيحتان أيضاً، لكن لا يُنظر إليهما أبداً.
وهنا أكبر فخ
لأن elif تتوقف عند أول شرط صحيح، فإن الخطأ في الترتيب ينتج برنامجاً يعمل، ولا يُظهر أي خطأ، ويجيب إجابة خاطئة:
marks = 95
if marks >= 40:
grade = "Pass"
elif marks >= 80:
grade = "Excellent"
else:
grade = "Fail"
print(grade)Pass95 درجة ولا Excellent. لأن 95 >= 40 كانت صحيحة فوراً، وتوقف بايثون عندها — ولم يُفحص الشرط الثاني أبداً.
القاعدة التي يجب تذكّرها: ضع الشرط الأشد والأكثر تحديداً أولاً، والأكثر تساهلاً أخيراً. عند العمل مع نطاقات رقمية، انزل من الأكبر إلى الأصغر، كما في مثال الدرجات أعلاه.
هذا الخطأ البرمجي صامت لدرجة أن رؤيته بعينك هي الطريقة الوحيدة لاكتشافه. لذلك اجعلها عادة: بعد كتابة سلسلة شروط، شغّلها مرة بقيمة لكل فرع. يجب أن يعطي كود الدرجات أعلاه خمس إجابات مختلفة للقيم 95 و 72 و 65 و 45 و 20. إذا تطابقت أي إجابتين، فالترتيب خاطئ.
شرط داخل شرط
is_logged_in = True
is_admin = False
if is_logged_in:
if is_admin:
print("Welcome, admin")
else:
print("Welcome, user")
else:
print("Please log in")Welcome, userif الداخلية مُزاحة بأربع مسافات، لأنها تقع تحت الخارجية، وأسطر print الخاصة بها مُزاحة بثماني مسافات. هذا يعمل، لكن كلما زادت المستويات صعبت قراءة الكود. وغالباً ما تقول and الشيء نفسه بوضوح أكبر:
is_logged_in = True
is_admin = False
if is_logged_in and is_admin:
print("Welcome, admin")
elif is_logged_in:
print("Welcome, user")
else:
print("Please log in")Welcome, userأيهما أفضل يعتمد على أيهما أسهل قراءة. إذا وجدت نفسك على عمق أكثر من ثلاثة مستويات، فتوقف وأعد التفكير — فهذه علامة شبه دائمة على أنه يمكن ترتيب الشروط بطريقة أخرى.
التحقق من الإدخال
لدينا الآن الأداة التي تحل المشكلة التي بقيت مفتوحة في فصل سابق — ماذا نفعل عندما لا يكتب المستخدم شيئاً، أو يكتب كلمة:
raw = input("Your age: ").strip()
if raw == "":
print("You did not type anything")
elif raw.isdigit():
age = int(raw)
if age >= 18:
print(f"Age {age}: you may enter")
else:
print(f"Age {age}: adults only")
else:
print(f"'{raw}' is not a whole number")Your age: twenty
'twenty' is not a whole numberisdigit() طريقة (method) جديدة — تخبرك هل كل أحرف السلسلة النصية أرقام. هذه هي الطريقة البسيطة للتحقق قبل تمرير أي شيء إلى int()، وبها يختفي ValueError. (لا تقبل isdigit() الأعداد السالبة ولا العشرية — فهذه تحتاج إلى try، الذي سيأتي لاحقاً.)
مثال كامل
fare.py:
# Work out a ticket fare from age and day of week
age = int(input("Passenger age : "))
day = input("Day of week : ").strip().lower()
base_fare = 120.0
# Most specific rule first, loosest last
if age < 5:
fare = 0.0
category = "Infant"
elif age < 18:
fare = base_fare * 0.5
category = "Child"
elif age >= 60:
fare = base_fare * 0.7
category = "Senior"
else:
fare = base_fare
category = "Adult"
is_weekend = day == "saturday" or day == "sunday"
if is_weekend:
fare = fare * 1.2
print()
print(f"Category : {category}")
print(f"Weekend : {is_weekend}")
print(f"Fare : {fare:.2f}")Passenger age : 65
Day of week : Sunday
Category : Senior
Weekend : True
Fare : 100.80لاحظ قرارين في التصميم. الأول: شروط العمر مربوطة بـ elif، لأن المسافر يقع في فئة واحدة بالضبط. الثاني: شرط عطلة نهاية الأسبوع if منفصلة، لأنه سؤال مستقل — مهما كان العمر، تُضاف زيادة عطلة نهاية الأسبوع فوق ذلك. تحديد ما يكون elif وما يكون if منفصلة هو العمل الحقيقي في كتابة الشروط.
حالات الخطأ الشائعة وحلولها
SyntaxError: expected ':' النقطتان في نهاية سطر if أو elif أو else مفقودتان.
IndentationError: expected an indented block after 'if' statement السطر الذي بعد if غير مُزاح. يحتاج الشرط إلى سطر واحد على الأقل داخله.
IndentationError: unexpected indent أحد الأسطر مُزاح بلا سبب. حدّد إلى أي كتلة يُفترض أن ينتمي هذا السطر.
TabError: inconsistent use of tabs and spaces in indentation علامات الجدولة والمسافات مخلوطة في ملف واحد. شغّل "convert indentation to spaces" في محررك.
SyntaxError — elif أو else وحدها elif و else تنتميان دائماً إلى if، ويجب أن تكونا على مستوى الإزاحة نفسه لتلك الـ if.
TypeError: '>=' not supported between instances of 'str' and 'int' نسيت تحويل قيمة input(). اكتب int(input(...)).
الشروط تبدو صحيحة لكنها تسلك المسار الخطأ افحص الترتيب. إذا جاء الشرط المتساهل أولاً فلن يُفحص الشرط الصارم أبداً. شغّل البرنامج مرة بقيمة لكل فرع.
الخطوة ٤ من ٦ — التوقع
تحقّق من فهمك
مع 85 درجة، ماذا يطبع هذا البرنامج؟
marks = 85
if marks >= 40:
print("Pass")
elif marks >= 80:
print("Excellent")
else:
print("Fail")- AExcellent Pass
- BPass
- CPass Excellent
- DExcellent
ماذا يطبع هذا البرنامج؟
score = 30
if score >= 50:
print("Passed")
print("Done")- APassed Done
- BDone
- Cلا شيء إطلاقاً
- DPassed
حتى عندما يكتب المستخدم 25، يتوقف البرنامج بخطأ TypeError. ما السبب، وما الحل؟
age = input("Age: ")
if age >= 18:
print("Adult")
else:
print("Minor")- A`input()` تعطي نصاً، ولا يمكن مقارنة النص برقم بهذه الطريقة — يجب كتابة `age = int(input("Age: "))`
- Bيجب وجود `elif` قبل `else`
- Cلا يمكن استخدام `>=` في شرط `if`؛ يجب أن تكون `>`
- D`age` كلمة محجوزة، لذلك فشلت المقارنة
الإجابة تتطلب حساباً
سجّل الدخول للتحقق من إجاباتك
الأسئلة موجودة أعلاه، والتفكير في إجاباتها ذهنياً هو الجزء المهم. سجّل الدخول لرؤية الإجابات والتفسيرات والتلميحات ذات المستويات الثلاثة.
دورك الآن
اكتب ملفاً باسم bmi.py يأخذ الطول بالأمتار والوزن بالكيلوغرامات، ويحسب مؤشر كتلة الجسم (BMI)، ويعرض الفئة.
المعادلة: bmi = weight / (height * height)
الفئات:
- أقل من 18.5 — Underweight
- 18.5 أو أكثر، لكن أقل من 25 — Normal
- 25 أو أكثر، لكن أقل من 30 — Overweight
- 30 أو أكثر — Obese
الحدود مكتوبة عمداً بـ "أقل من" (<) و "أو أكثر" (>=). كتابة "من 18.5 إلى 24.9" تترك فجوة في الوسط — فقيمة مثل 24.95 لن تقع حينها في أي فئة. مع < و >= يقع كل رقم في فئة واحدة بالضبط.
أربعة شروط:
- اعرض BMI بخانة عشرية واحدة
- رتّب الشروط بحيث تعمل الفئات الأربع كلها فعلاً
- إذا كان الطول
0، فتوقف قبل القسمة واطبع رسالة — وإلا ستحصل علىZeroDivisionError - شغّله بأربع قيم تغطي الفئات الأربع، وأيضاً بقيم تقع على الحدود تماماً — في أي فئة يقع BMI يساوي 25 بالضبط؟ (يجب أن تكون Overweight.)
الشرط الرابع هو الأهم. القيم التي تقع على الحدود تماماً هي حيث تظهر أخطاء ترتيب الشروط.
في هذه المهمة المزيد
ما قرأته أعلاه هو المهمة كاملة — لم يُحذف منها شيء. يضيف Pro:
- ما يحدث فعلياً في الكواليس — كيف ينفذ بايثون هذا الكود
- الحل الكامل للتمرين مشروحاً خطوة بخطوة
- تمارين إضافية أكثر صعوبة وتحدياً
- أسئلة المقابلات الوظيفية حول هذا الموضوع مع نموذج الإجابة
الخطوة ٦ من ٦
التحدي — اختبار الفصل
عشرة أسئلة متدرجة من السهل إلى الصعب. الأسئلة الأخيرة صعبة عن قصد.
سجّل الدخول لأداء الاختبار