Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 8

معالجة الأخطاء بـ try/except

Recommended resources

أهلاً بك في الدرس الثامن والأخير من وحدة "أساسيات البرمجة بلغة بايثون".

في الدرس السابق، قمنا بتنظيم الكود الخاص بنا باستخدام الدوال (Functions)، مما سمح لنا بإعادة استخدام الكود وتجنب التكرار. لكن ماذا يحدث عندما نواجه مواقف غير متوقعة؟ على سبيل المثال، ماذا لو كانت الدالة تتوقع رقماً، ولكن المستخدم أدخل نصاً؟ في الوضع الحالي، سينهار البرنامج ويعرض رسالة خطأ غير مفهومة للمستخدم.

هدفنا اليوم هو تعلم كيفية التعامل مع الأخطاء الأساسية باستخدام كتل try/except. هذه التقنية ستجعل برامجنا أكثر قوة ومتانة، حيث سنتعلم كيف نتوقع الأخطاء ونتعامل معها بأناقة بدلاً من السماح لها بإيقاف البرنامج بالكامل. بنهاية هذا الدرس، ستكون قد أكملت جميع أساسيات بايثون التي تحتاجها للانتقال إلى مواضيع أكثر تقدماً.

1. ما هي الأخطاء في البرمجة؟

في بايثون، كما في معظم لغات البرمجة، يوجد نوعان رئيسيان من الأخطاء:

  1. الأخطاء النحوية (Syntax Errors): هي أخطاء في "قواعد اللغة". تحدث عندما تكتب كوداً لا يتبع بنية لغة بايثون الصحيحة، مثل نسيان نقطتين رأسيتين (:) بعد جملة if أو خطأ إملائي في كلمة مفتاحية. المترجم يكتشف هذه الأخطاء قبل تشغيل البرنامج.

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

    • محاولة القسمة على صفر (ZeroDivisionError).
    • محاولة تحويل نص غير رقمي إلى رقم (ValueError).
    • محاولة الوصول إلى عنصر غير موجود في قائمة (IndexError).

الاستثناءات هي ما سنتعلم كيفية "التقاطه" والتعامل معه اليوم.

الاخطاء والاستثناءات في بايثون

تقدم هذه المقالة من مدونة "أحمد عبيد" شرحاً واضحاً للفرق بين الأخطاء النحوية والاستثناءات. قراءتها ستمنحك أساساً نظرياً قوياً قبل أن ننتقل إلى التطبيق العملي.

اقرأ الأجزاء التالية من المقال: المقدمة: لفهم تعريف الاستثناء (Exception). خطأ بناء او صياغة الجملة البرمجية: لفهم الأخطاء النحوية (Syntax Errors). خطأ الاستثناءات: لرؤية أمثلة على الأخطاء التي تحدث أثناء التنفيذ، مثل ZeroDivisionError و TypeError.


2. التعامل مع الاستثناءات باستخدام try و except

لتجنب انهيار البرنامج عند حدوث استثناء، نستخدم كتلة try...except. الفكرة بسيطة:

  • try: نضع الكود الذي "نشك" أنه قد يسبب خطأ داخل هذه الكتلة.
  • except: نضع الكود الذي سيتم تنفيذه فقط في حالة حدوث خطأ داخل كتلة try.

إذا تم تنفيذ الكود في try بنجاح، يتم تجاهل كتلة except تماماً.

Basic Try-Except Structure in Python
هذا الرسم يوضح الهيكل البسيط لكتلة `try-except`. الشيفرة التي قد تسبب خطأ توضع في `try`، والشيفرة التي تعالج الخطأ توضع في `except`.

لنفترض أننا نكتب برنامجاً يطلب من المستخدم عمره:

age_input = input("الرجاء إدخال عمرك: ")
age = int(age_input) # هذا السطر قد يسبب خطأ
print(f"عمرك هو {age} عاماً.")

إذا أدخل المستخدم "ثلاثون" بدلاً من "30"، سيحدث خطأ ValueError وينهار البرنامج. لنصلح هذا باستخدام try...except:

try:
    age_input = input("الرجاء إدخال عمرك: ")
    age = int(age_input) # نضع الكود "الخطير" هنا
    print(f"عمرك هو {age} عاماً.")
except:
    # هذا الكود ينفذ فقط إذا فشل الكود في try
    print("إدخال غير صالح. الرجاء إدخال أرقام فقط.")

print("...يستمر البرنامج في العمل.")

الآن، إذا أدخل المستخدم "ثلاثون"، لن ينهار البرنامج. بدلاً من ذلك، سيتم طباعة رسالة ودية، وسيستمر البرنامج في التنفيذ.

الأخطاء البرمجية | معالجة الإستناءات في بايثون للمبتدئين

يشرح هذا الفيديو من قناة Python Arabic Community أهمية معالجة الاستثناءات لمنع انهيار البرنامج وتقديم تجربة أفضل للمستخدم. شاهد كيف يتغير سلوك البرنامج قبل وبعد استخدام try-except.

شاهد المقطع من الدقيقة 6:56 حتى 9:06. ركز على فهم لماذا نحتاج لمعالجة الأخطاء، وكيف تمنع كتلة try-except توقف البرنامج المفاجئ.


3. معالجة أنواع محددة من الاستثناءات

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

لنفكر في برنامج القسمة الكلاسيكي:

try:
    numerator = int(input("أدخل البسط: "))
    denominator = int(input("أدخل المقام: "))
    result = numerator / denominator
    print(f"النتيجة هي: {result}")

except ValueError:
    print("خطأ: الرجاء إدخال أرقام صحيحة فقط.")

except ZeroDivisionError:
    print("خطأ: لا يمكن القسمة على صفر.")

في هذا المثال:

  • إذا أدخل المستخدم نصاً، سيتم التقاط ValueError وعرض الرسالة الأولى.
  • إذا أدخل المستخدم 0 في المقام، سيتم التقاط ZeroDivisionError وعرض الرسالة الثانية.

هذا يجعل برنامجك أكثر ذكاءً وقدرة على توجيه المستخدم بشكل أفضل.

الاخطاء والاستثناءات في بايثون

لنعد إلى مقال "أحمد عبيد" لنرى كيفية تطبيق هذه المفاهيم بشكل عملي. هذه الأقسام تغطي بناء جملة try-except وكيفية استخدام كتل except متعددة.

اقرأ الأجزاء التالية: كيفية التعامل مع الاستثناءات في بايثون: لفهم بنية try-except الأساسية. كيفية تنفيذ نوع معين من الاستثناءات: لتعلم كيفية التقاط خطأ محدد مثل NameError. استخدام استثناءات متعددة في بايثون: لرؤية مثال يجمع بين معالجة ZeroDivisionError و ValueError.

اختبر فهمك!

لديك القائمة التالية: my_list = [10, 20, 30]. اكتب كتلة try...except تطلب من المستخدم إدخال فهرس (index)، ثم تطبع العنصر الموجود في ذلك الفهرس. يجب أن يعالج برنامجك خطأين محتملين:

  1. ValueError: إذا أدخل المستخدم شيئًا ليس رقمًا.
  2. IndexError: إذا أدخل المستخدم فهرسًا خارج نطاق القائمة (مثل 3 أو -4).
أظهر الحل
my_list = [10, 20, 30]

try:
    index_str = input("أدخل الفهرس الذي تريد عرضه: ")
    index = int(index_str)
    print(f"العنصر في الفهرس {index} هو: {my_list[index]}")

except ValueError:
    print("خطأ: يجب إدخال رقم صحيح.")

except IndexError:
    print(f"خطأ: الفهرس غير صالح. الفهارس المتاحة هي من 0 إلى {len(my_list) - 1}.")

جرب تشغيل هذا الكود وإدخال "واحد"، ثم "3"، ثم "1" لترى كيف يستجيب لكل حالة.


4. الجمل الإضافية: else و finally

كتلة try...except يمكن أن تحتوي أيضاً على جزأين اختياريين: else و finally.

  • else: الكود الموجود داخل كتلة else يتم تنفيذه فقط إذا لم يحدث أي استثناء في كتلة try. هذا مفيد لفصل الكود الذي يجب أن يعمل فقط عند النجاح.

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

Python Try-Except-Finally Example
هذا المثال يوضح أن كتلة `finally` تُنفذ دائماً. في الكود، حدث خطأ لأن المتغير `x` غير معرف، فتم تنفيذ `except`. بعد ذلك، تم تنفيذ `finally` في كل الأحوال.

لنرى مثالاً يجمعهم كلهم:

try:
    file = open("my_data.txt", "r") # محاولة فتح ملف للقراءة
    content = file.read()
except FileNotFoundError:
    print("خطأ: الملف غير موجود.")
else:
    # هذا ينفذ فقط إذا تم فتح الملف بنجاح
    print("محتويات الملف:")
    print(content)
finally:
    # هذا ينفذ دائماً، للتأكد من إغلاق الملف إذا تم فتحه
    if 'file' in locals() and not file.closed:
        file.close()
        print("تم إغلاق الملف بنجاح.")

فكرة finally مهمة جداً وستستخدمها بكثرة عند التعامل مع الموارد الخارجية في الدروس القادمة.


ملخص الدرس

تهانينا! لقد أكملت الوحدة الأولى من رحلتنا. لقد قمت ببناء أساس متين في بايثون سيخدمك جيداً في كل ما هو قادم.

في هذا الدرس، تعلمنا:

  • هناك نوعان من الأخطاء: الأخطاء النحوية (Syntax Errors) والاستثناءات (Exceptions).
  • نستخدم كتلة try...except "لالتقاط" الاستثناءات ومنع البرنامج من الانهيار.
  • من الأفضل معالجة أنواع محددة من الاستثناءات (ValueError, ZeroDivisionError, إلخ) لتقديم استجابات دقيقة.
  • كتلة else تُنفذ عند نجاح كتلة try بدون أخطاء.
  • كتلة finally تُنفذ دائماً، وهي مثالية لعمليات التنظيف.

ما التالي؟

لقد أنهيت بنجاح وحدة "أساسيات البرمجة بلغة بايثون". أنت الآن جاهز للانتقال إلى المستوى التالي. في الوحدة القادمة، "العمل مع البيانات وواجهات برمجة التطبيقات (APIs)"، سنبدأ في جعل برامجنا تتفاعل مع العالم الخارجي. الدرس الأول سيكون عن تثبيت واستيراد المكتبات الخارجية، والتي تفتح لنا عالماً جديداً من الإمكانيات الجاهزة التي يمكننا إضافتها إلى برامجنا. المهارات التي تعلمتها اليوم في معالجة الأخطاء ستكون لا غنى عنها هناك، لأن التعامل مع الملفات والشبكات مليء بالمفاجآت والأخطاء المحتملة