2.11. العبارات الشرطية

العبارة الشرطية تشغّل كتلة من التعليمات البرمجية فقط عندما يُقيَّم اختبار ما إلى صحيح. والكلمة المفتاحية هي if، وقد يتبعها اختياريًا فرع elif واحد أو أكثر ("else if") وفرع else نهائي.

n = 42

if n > 0:
    print("positive")
elif n < 0:
    print("negative")
else:
    print("zero")

متن كل فرع هو كل ما هو مزاح للداخل تحته (أربع مسافات حسب العُرف). يمر Python على الفروع بالترتيب، ويشغّل أول فرع اختباره صحيح، ويتخطى البقية. ولا تُشغّل كتلة else إلا إذا كان كل اختبار سابق خاطئًا؛ وهي اختيارية دائمًا.

A flowchart showing if/elif/else: two diamond decision tests with their bodies, falling through to a final else body when both tests are false.

فرع واحد فقط يُشغَّل على الإطلاق. تُقيَّم الاختبارات من الأعلى إلى الأسفل حتى ينجح أحدها؛ ويُتخطى الباقي.

2.11.1. الصحة المنطقية

لا يلزم أن يُعيد الاختبار في if القيمة True أو False -- إذ تُحسب أي قيمة إما صحيحة منطقيًا أو خاطئة منطقيًا. والقيم الخاطئة منطقيًا هي:

  • False و None

  • العدد صفر: 0 و 0.0

  • التسلسلات والتجميعات الفارغة: "" و [] و () و {} و b""

كل ما عدا ذلك صحيح منطقيًا. وهذا يتيح لك كتابة اختبارات مُقتضبة:

if name:                     # false on empty string
    print("hello", name)

if items:                    # false on empty list, dict, etc.
    process(items)

انتبه إلى أن الصحة المنطقية تغيّر المعنى. فـ if value: ليست كـ if value is not None: -- إذ إن الأولى تكون خاطئة أيضًا عندما يكون value هو 0 أو "". وعندما تعني حقًا "هل هذا بالضبط None"، استخدم is None / is not None صراحةً.

2.11.2. التعبيرات الثلاثية

يمكن أن تظهر العبارة الشرطية داخل تعبير:

label = "even" if n % 2 == 0 else "odd"

تُقرأ "label هو "even" إذا كان n % 2 == 0 وإلا "odd"". مفيدة للأسطر المفردة؛ أما لأي شيء يتجاوز سطرًا واحدًا، فعبارة if الكاملة أسهل في القراءة.

2.11.3. التداخل والإرجاعات المبكرة

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

def process(item):
    if item is not None:
        if item.is_valid():
            if item.size() > 0:
                if item.owner == "me":
                    return do_the_work(item)
    return None

يفلطح نمطان هذا النوع من التعليمات البرمجية.

2.11.3.1. الإرجاعات المبكرة للحراسات

عالِج كل حالة "انسحاب" أولًا، كل منها بـ return خاص بها، حتى يبقى المنطق الرئيسي عند الإزاحة الخارجية. وتُقرأ كل حراسة كـ"هذه ليست حالة نعالجها؛ غادِر":

def process(item):
    if item is None:
        return None
    if not item.is_valid():
        return None
    if item.size() == 0:
        return None
    if item.owner != "me":
        return None
    return do_the_work(item)

أصبح "المسار الرئيسي" الآن سطرًا واحدًا في أسفل الدالة، لا مدفونًا داخل أربع طبقات. ويُسمى هذا الأسلوب أحيانًا نمط عبارة الحراسة (guard clause).

2.11.3.2. دمج الاختبارات بـ and / or

عندما يجب أن تتحقق عدة شروط جميعها للفرع نفسه، ادمجها بـ and بدلًا من التداخل. أما الشروط التي يُطلق كل منها الفرع باستقلالية فتُدمج بـ or:

# all must hold -- use `and`
if user.is_admin() and user.has_permission("write") and not locked:
    save()

# any one of them is enough -- use `or`
if c == " " or c == "\t" or c == "\n":
    whitespace_count += 1

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