6. الاستثناءات
ننتقل الآن إلى الاستثناءات.
![]() |
يوضح البرنامج النصي الأول ضرورة معالجة الاستثناءات.
# -*- coding=utf-8 -*-
# يحدث خطأ
x=4/0
نقوم بإحداث خطأ متعمد لرؤية المعلومات التي ينتجها المترجم. والنتيجة التي تظهر على الشاشة هي كما يلي:
يقدم لنا السطر 4:
- نوع الاستثناء: ZeroDivisionError؛
- رسالة الخطأ المرتبطة: integer division or modulo by zero. وهي باللغة الإنجليزية. وهذا أمر قد نرغب في تغييره.
القاعدة الأساسية في البرمجة هي أنه يجب بذل كل جهد ممكن لتجنب حالات التعطل «العشوائية» مثل تلك المذكورة أعلاه. حتى في حالة حدوث خطأ، يجب أن ينتهي البرنامج بشكل سليم مع تقديم معلومات عن الخطأ الذي وقع.
صيغة إدارة الاستثناءات هي كما يلي:
try:
actions susceptibles de lancer une exception
except [classe d'exception, ...]:
actions de gestion de l'exception
finally:
actions toujours exécutées qu'il y ait exception ou non
في جملة try، يتوقف تنفيذ الإجراءات فور حدوث استثناء. في هذه الحالة، يستمر التنفيذ مع الإجراءات الواردة في جملة except.
جملة
except [MyException, ...]:
تستقبل الاستثناءات من النوع MyException أو مشتقاتها. عندما يحدث استثناء داخل جملة try، يقوم المُفسر بفحص جمل except المرتبطة بجملة try بالترتيب الذي كُتبت به. ويتوقف عند أول جملة except تسمح بمعالجة الاستثناء الذي حدث. وإذا لم يعثر على أي منها، فإن الاستثناء ينتقل إلى الدالة المستدعية. وإذا كانت هذه الدالة تحتوي على جملة try / except، يتم معالجة الاستثناء مرة أخرى؛ وإلا فإنه يستمر في الانتقال عبر سلسلة الدوال المستدعاة. وفي نهاية المطاف، يصل الاستثناء إلى مترجم Python. عندئذٍ يقوم المترجم بإيقاف البرنامج قيد التنفيذ وعرض رسالة خطأ من النوع الموضح في المثال السابق. لذا فإن القاعدة هي أن البرنامج الرئيسي يجب أن يتعامل مع جميع الاستثناءات التي قد تنتقل من الطرق المستدعاة.
يحمل الاستثناء معه معلومات عن الخطأ الذي حدث. ويمكن الحصول على هذه المعلومات باستخدام الصيغة التالية:
except MyException as informations:
«informations» عبارة عن توبول يحمل المعلومات المتعلقة بالاستثناء.
الصيغة
except MyException, erreur:
تقوم بتعيين رسالة الخطأ المرتبطة بالاستثناء إلى المتغير erreur.
لإطلاق استثناء، نستخدم الصيغة
raise MyException(param1, param2, ...)
حيث تكون MyException في أغلب الأحيان فئة مشتقة من الفئة Exception. وستكون المعلمات التي يتم تمريرها إلى منشئ الفئة متاحة في جملة except في هياكل اعتراض الاستثناءات.
يتم توضيح هذه المفاهيم من خلال البرنامج النصي التالي.
يعالج البرنامج النصي التالي الأخطاء بشكل صريح:
# -*- coding=utf-8 -*-
i=0
# يتم إحداث خطأ ومعالجته
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# لم تتغير قيمة x
print "x=%s" % (x)
# نعيد المحاولة
i+=1
try:
x=4/0
except Exception, erreur:
# نقوم باعتراض الاستثناء الأكثر عمومية
print ("%s : %s ") % (i, erreur)
# يمكننا اعتراض عدة أنواع من الاستثناءات
i+=1
try:
x=4/0
except ValueError, erreur:
# هذا الاستثناء لا يحدث هنا
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# نقوم باعتراض الاستثناء الأكثر عمومية
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# يتم اعتراض نوع محدد
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# نعيد المحاولة بتغيير الترتيب
i+=1
try:
x=4/0
except ValueError, erreur:
# هذا الاستثناء لا يحدث هنا
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# يتم اعتراض نوع محدد
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# يتم اعتراض الاستثناء الأكثر عمومية
print ("%s : (Exception) %s ") % (i, erreur)
# جملة except بدون معلمات
i+=1
try:
x=4/0
except:
# لا نهتم بطبيعة الاستثناء ولا برسالة الخطأ
print ("%s : il y a eu un probleme ") % (i)
# نوع آخر من الاستثناءات
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# استثناء يحمل معلومات في مجموعة بيانات يمكن للبرنامج الوصول إليها
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# يمكن إثارة الاستثناءات
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# يمكن إنشاء استثناءات خاصة
class MyError(Exception):
pass
# يتم إطلاق الاستثناء MyError
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# يتم إطلاق الاستثناء MyError
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# يمكن إلقاء أي نوع من الكائنات
class Objet:
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
i+=1
try:
raise Objet("pb...")
except Objet as erreur:
print "%s : %s" % (i, erreur)
# يتم دائمًا تنفيذ جملة finally
# سواء حدث استثناء أم لا
i+=1
x=None
try:
x=1
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
i+=1
x=None
try:
x=2/0
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
ملاحظات:
- الأسطر 6-9: يتم التعامل مع حالة القسمة على الصفر؛
- السطر 8: يتم اعتراض الاستثناء المحدد الذي حدث؛
- السطر 11: بسبب الاستثناء الذي حدث، لم تتلقى x أي قيمة، وبالتالي لم تتغير قيمتها؛
- الأسطر 15-19: نكرر نفس الإجراء ولكن مع اعتراض استثناء من مستوى أعلى من النوع Exception. ونظرًا لأن الاستثناء ZeroDivisionError مشتق من الفئة Exception، فإن الجملة except ستوقفه؛
- الأسطر 23-33: يتم وضع عدة جمل except للتعامل مع أنواع متعددة من الاستثناءات. سيتم تنفيذ جملة except واحدة فقط أو لن يتم تنفيذ أي منها إذا لم يتطابق الاستثناء مع أي جملة except؛
- الأسطر 51-55: قد لا تحتوي الجملة except على أي وسيطة. في هذه الحالة، فإنها توقف جميع الاستثناءات؛
- الأسطر 59-62: تُدرج الاستثناء ValueError؛
- الأسطر 66-69: يتم استرداد المعلومات التي تحملها الاستثناء؛
- الأسطر 73-76: توضح طريقة إثارة استثناء؛
- الأسطر 79-84: توضح استخدام فئة استثناء خاصة MyError؛
- الأسطر 79-80: تكتفي الفئة MyError بالاشتقاق من الفئة الأساسية Exception. وهي لا تضيف أي شيء إلى فئتها الأساسية. لكن الآن، يمكن تسميتها صراحةً في جمل except؛
- الأسطر 97-107: توضح أنه في لغة Python يمكن في الواقع إلقاء أي نوع من الكائنات وليس فقط الكائنات المشتقة من الفئة Exception؛
- الأسطر 109-127: توضح استخدام جملة finally.
نتائج العرض على الشاشة هي كما يلي:
0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None
يوضح هذا البرنامج النصي الجديد كيفية تمرير الاستثناءات عبر سلسلة الطرق المستدعية:
# -*- coding=utf-8 -*-
# استثناء خاص
class MyError(Exception):
pass
# ثلاث طرق
def f1(x):
# لا تتم معالجة الاستثناءات — يتم تمريرها تلقائيًا
return f2(x)
def f2(y):
# لا يتم التعامل مع الاستثناءات - يتم تمريرها تلقائيًا
return f3(y)
def f3(z):
if (z % 2) == 0:
# إذا كان z زوجيًا، يتم إلقاء استثناء
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# تنتقل الاستثناءات عبر سلسلة الطرق التي تم استدعاؤها
# حتى تعترضها إحدى الطرق. وهنا ستكون الطريقة هي main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# يمكن لأي دالة أن تضيف معلومات إلى الاستثناءات التي ترسلها
def f4(x):
try:
return f5(x)
except MyError as infos:
# يتم إثراء الاستثناء ثم إعادة إطلاقه
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# نقوم بإثراء الاستثناء ثم نعيد إطلاقه
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# نقوم بإطلاق استثناء
raise MyError("exception dans f6")
else:
return 2*z
#---------- main
try:
print f4(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
ملاحظات:
- في الأسطر 27-30، في استدعاء main --> f1 --> f2 --> f3 (السطر 28)، ستنتقل الاستثناء MyError الذي أطلقه f3 إلى main. وستتم معالجتها عندئذٍ بواسطة الجملة except في السطر 29؛
- الأسطر 55-58: في الاستدعاء main --> f4 --> f5 --> f6 (السطر 56)، ستنتقل الاستثناء MyError الذي أطلقته f6 إلى main. ثم ستتم معالجتها بواسطة الجملة except في السطر 29. هذه المرة، أثناء صعودها عبر سلسلة الطرق المستدعية، يتم إثراء الاستثناء MyError بمعلومات تضافها كل طريقة يتم المرور بها.
نتائج العرض على الشاشة هي كما يلي:
