2.25. معالجة الأخطاء

تظهر معظم مشكلات وقت التشغيل في Python على هيئة استثناءات -- وهي طريقة مُسمّاة ومنظّمة للإبلاغ عن حدوث خطأ ما. و ValueError و TypeError و KeyError و OSError و MemoryError كلها أمثلة؛ كل منها صنف (class)، وإطلاق واحد منها يوقف الاستدعاء الحالي ويبحث عن معالج في الشيفرة المحيطة.

2.25.1. try / except

غلّف كتلة من الشيفرة في try لالتقاط أي استثناء تطلقه:

try:
    n = int(input_text)
except ValueError:
    n = 0

إذا فشل تحويل int(...)، ينتقل التحكّم إلى كتلة except بدلًا من نشر الخطأ أبعد من ذلك. وإذا كانت input_text سلسلة عدد صحيح صالحة، تُتخطّى كتلة except.

يمكن أن تحتوي كتلة try واحدة على عدة كتل except، تلتقط كل منها نوعًا مختلفًا من الأخطاء:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

تطابق Python بالترتيب؛ ويعالج المشكلةَ أولُ معالج يناسب صنف استثنائه. والتقاط Exception (الصنف الأساسي لكل شيء تقريبًا) يعالج أي خطأ؛ احتفظ بهذا للطبقة الخارجية من البرنامج حيث يكون البديل هو الانهيار.

تحذير

تلتقط كتلة except: المجرّدة (بدون صنف بعد الكلمة المفتاحية) أيضًا KeyboardInterrupt -- وهو الاستثناء الذي يرسله IDE عند الضغط على زر stop لمقاطعة برنامج نصي قيد التشغيل. والحلقة المُغلّفة بـ except: pass مجرّدة ستبتلع المقاطعة وتستمر في العمل، دون أن تترك أي طريقة لإيقاف البرنامج النصي سوى إعادة تشغيل الطاقة.

فضّل except Exception: على except: المجرّدة عندما تحتاج فعلًا إلى التقاط واسع. فـ KeyboardInterrupt يرث من BaseException وليس من Exception، لذا يُبقي except Exception: زر الإيقاف يعمل.

2.25.1.1. فحص الاستثناء

لقراءة الرسالة المرفقة باستثناء، سمِّه باستخدام as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

المتغيّر المربوط بـ as صالح فقط داخل كتلة except.

2.25.2. else و finally

لكتلة try إضافتان اختياريتان.

تعمل else فقط عندما تنتهي كتلة try دون إطلاق أي استثناء:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

وضع "ما يجب فعله عند النجاح" في else يُبقي كتلة try ضيّقة -- إذ لا ينتمي إلى try سوى السطر الذي قد يفشل.

تعمل finally في النهاية مهما حدث -- سواء نجحت كتلة try، أو أطلقت استثناءً وعُولج، أو أطلقت استثناءً على وشك الانتشار:

try:
    do_work()
finally:
    cleanup()
Flow diagram showing the four possible paths through try / except / else / finally: success goes to else then finally; a caught exception goes to except then finally; an uncaught exception goes to finally then propagates.

تعمل finally دائمًا. وتعمل else فقط في مسار عدم الاستثناء.

بالنسبة لمعظم أنماط الاكتساب/التحرير، فضّل مدير سياق على زوج try / finally -- إذ يدير المورد نفسه عملية تنظيفه الخاصة.

2.25.3. الاستثناءات المدمجة الشائعة

قائمة قصيرة بالاستثناءات التي ستصادفها كثيرًا:

  • ValueError -- النوع صحيح والقيمة خاطئة (bytes([300]) -- 300 هو النوع الصحيح، لكنه خارج نطاق البايت الصالح 0..255).

  • TypeError -- النوع خاطئ تمامًا (len(42)).

  • KeyError -- مفتاح مفقود في dict.

  • IndexError -- مؤشّر يتجاوز نهاية التسلسل.

  • AttributeError -- الوصول إلى سمة غير موجودة ("abc".foo).

  • OSError -- فشل في نظام الملفات أو في الإدخال/الإخراج.

  • MemoryError -- نفاد الكومة (heap). على وقت تشغيل محدود الذاكرة، يمكن أن يحدث هذا أثناء التشغيل العادي -- وليس فقط في الحالات المرضية.