معالجة الأخطاء والاستثناءات في بايثون

معالجة الأخطاء والاستثناءات في بايثون

45 دقيقة
١١ يونيو ٢٠٢٦
المرحلة 1 من 6

ما هي الأخطاء والاستثناءات؟

ما هي الأخطاء والاستثناءات في بايثون؟

عندما تبدأ في كتابة برامج بايثون، ستواجه حتماً مواقف يتوقف فيها البرنامج فجأة عن العمل ويعرض رسالة خطأ. هذه هي "الاستثناءات" (Exceptions). في هذا الدرس، سنتعلم ما هي الأخطاء والاستثناءات، ولماذا تحدث، وكيف نتعامل معها بذكاء لجعل برامجنا أكثر قوة واستقراراً.

لماذا نحتاج إلى معالجة الأخطاء؟

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

أنواع الأخطاء في بايثون

هناك نوعان رئيسيان من الأخطاء:

  • Syntax Errors (أخطاء نحوية): تحدث عندما يخالف الكود قواعد لغة بايثون. مثلاً نسيان النقطتين بعد تعريف دالة.
  • Exceptions (استثناءات): تحدث أثناء تشغيل البرنامج حتى لو كان الكود صحيحاً نحوياً. مثل قسمة عدد على صفر أو محاولة فتح ملف غير موجود.

أمثلة شائعة على الاستثناءات

دعنا نرى بعض الاستثناءات التي ستواجهها كثيراً:

# ZeroDivisionError: القسمة على صفر
result = 10 / 0  # سيتوقف البرنامج هنا

# ValueError: قيمة غير صالحة
number = int("abc")  # لا يمكن تحويل "abc" إلى رقم

# FileNotFoundError: ملف غير موجود
file = open("non_existent.txt", "r")  # الملف غير موجود

# IndexError: مؤشر خارج نطاق القائمة
my_list = [1, 2, 3]
print(my_list[5])  # القائمة تحتوي فقط على 3 عناصر

# KeyError: مفتاح غير موجود في القاموس
my_dict = {"name": "Ahmed"}
print(my_dict["age"])  # المفتاح "age" غير موجود

كيف نتعامل مع الاستثناءات؟

لحسن الحظ، توفر بايثون أدوات قوية للتعامل مع الاستثناءات باستخدام try و except.

1. البنية الأساسية: try-except

try:
    # الكود الذي قد يسبب خطأ
    number = int(input("أدخل رقماً: "))
    result = 10 / number
    print(f"النتيجة: {result}")
except ZeroDivisionError:
    # ماذا تفعل إذا حدث خطأ القسمة على صفر
    print("خطأ: لا يمكن القسمة على صفر!")
except ValueError:
    # ماذا تفعل إذا أدخل المستخدم نصاً بدلاً من رقم
    print("خطأ: يرجى إدخال رقم صحيح")

2. استخدام except عام (غير موصى به)

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

try:
    number = int(input("أدخل رقماً: "))
    result = 10 / number
except:
    print("حدث خطأ ما!")  # لا نعرف نوع الخطأ بالضبط

3. استخدام else و finally

else ينفذ إذا لم يحدث أي خطأ، و finally ينفذ دائماً بغض النظر عن حدوث خطأ:

try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("الملف غير موجود. سيتم إنشاؤه.")
    file = open("data.txt", "w")
    file.write("محتوى افتراضي")
else:
    print("تم قراءة الملف بنجاح!")
    print(content)
finally:
    file.close()
    print("تم إغلاق الملف")

مثال عملي متكامل: تطبيق آلة حاسبة بسيطة

لنطبق ما تعلمناه في مثال عملي:

def simple_calculator():
    print("آلة حاسبة بسيطة")
    print("العمليات المتاحة: +, -, *, /")
    
    try:
        num1 = float(input("أدخل الرقم الأول: "))
        operation = input("أدخل العملية (+, -, *, /): ")
        num2 = float(input("أدخل الرقم الثاني: "))
        
        if operation == "+":
            result = num1 + num2
        elif operation == "-":
            result = num1 - num2
        elif operation == "*":
            result = num1 * num2
        elif operation == "/":
            if num2 == 0:
                raise ZeroDivisionError("لا يمكن القسمة على صفر!")
            result = num1 / num2
        else:
            print("عملية غير معروفة!")
            return
        
        print(f"النتيجة: {num1} {operation} {num2} = {result}")
    
    except ValueError:
        print("خطأ: يرجى إدخال أرقام صحيحة")
    except ZeroDivisionError as e:
        print(f"خطأ رياضي: {e}")
    except Exception as e:
        print(f"حدث خطأ غير متوقع: {e}")
    finally:
        print("شكراً لاستخدام الآلة الحاسبة")

# تشغيل التطبيق
simple_calculator()

رفع الاستثناءات بنفسك (raise)

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

def validate_age(age):
    if age < 0:
        raise ValueError("العمر لا يمكن أن يكون سالباً!")
    if age > 150:
        raise ValueError("العمر كبير جداً!")
    return f"العمر {age} صحيح"

try:
    print(validate_age(-5))
except ValueError as e:
    print(f"خطأ في التحقق: {e}")

أخطاء شائعة يجب تجنبها

  • استخدام except عام بدون تحديد: يخفي الأخطاء ويجعل التصحيح صعباً.
  • عدم استخدام finally لإغلاق الموارد: قد يؤدي إلى تسرب الذاكرة أو مشاكل في الملفات.
  • تجاهل الاستثناءات تماماً: استخدام pass داخل except دون معالجة.
  • وضع كود كثير داخل try: اجعل كتلة try صغيرة قدر الإمكان لتحديد مصدر الخطأ بدقة.
💡 نصيحة مهمة: عند بناء API باستخدام FastAPI، ستستخدم معالجة الأخطاء بشكل مكثف. على سبيل المثال، عندما يطلب المستخدم مهمة غير موجودة، سترفع استثناء HTTPException بدلاً من ترك البرنامج يتعطل.

تمرين عملي

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

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

حاول كتابة الحل بنفسك قبل النظر إلى الإجابة أدناه:

def read_file_safely():
    while True:
        filename = input("أدخل اسم الملف: ")
        try:
            file = open(filename, "r")
            content = file.read()
            print("محتوى الملف:")
            print(content)
            break  # خروج من الحلقة إذا نجحت القراءة
        except FileNotFoundError:
            print(f"الملف '{filename}' غير موجود. حاول مرة أخرى.")
        except IOError:
            print(f"حدث خطأ في قراءة الملف '{filename}'.")
        finally:
            try:
                file.close()
            except NameError:
                pass  # الملف لم يفتح أصلاً

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

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

جاري تحميل التقييمات...