الاستثناءات
توقّع الأخطاء في برامجك وعالجها
معالجة الاستثناءات في بايثون
الاستثناءات هي أحداث تقع أثناء تنفيذ البرنامج وتُخلّ بالتسلسل الطبيعي للتعليمات. في بايثون، الاستثناءات هي كائنات تمثّل الأخطاء. وتُعدّ معالجة الاستثناءات أمرًا بالغ الأهمية لتطوير تطبيقات متينة قادرة على مواجهة المواقف غير المتوقعة دون أن تتعطل.
في هذا الدرس، سنستكشف كيفية تحديد الاستثناءات ومعالجتها وإنشائها في بايثون.
لماذا نعالج الاستثناءات؟
تكتسب معالجة الاستثناءات أهمية لعدة أسباب:
- المتانة: يستطيع البرنامج الذي يعالج الاستثناءات معالجة سليمة أن يواصل العمل حتى عند وقوع الأخطاء.
- تجربة المستخدم: عرض رسائل خطأ ودّية بدلًا من تتبّعات الأخطاء التقنية.
- تصحيح الأخطاء: الحصول على معلومات مفصّلة عن الخطأ لتسهيل إصلاحه.
- إدارة الموارد: التأكد من تحرير الموارد (الملفات، اتصالات الشبكة، إلخ) بشكل صحيح، حتى في حالة حدوث خطأ.
أنواع الاستثناءات الشائعة في بايثون
تتضمن بايثون العديد من أنواع الاستثناءات المدمجة. إليك بعضًا من أكثرها شيوعًا:
SyntaxError: خطأ في صياغة الشيفرة.NameError: محاولة استخدام متغيّر غير معرَّف.TypeError: تطبيق عملية أو دالة على كائن من نوع غير مناسب.ValueError: تتلقى عملية أو دالة وسيطًا من النوع الصحيح ولكن بقيمة غير مناسبة.IndexError: محاولة الوصول إلى فهرس غير موجود في تسلسل.KeyError: محاولة الوصول إلى مفتاح غير موجود في قاموس.FileNotFoundError: محاولة فتح ملف غير موجود.ZeroDivisionError: القسمة على صفر.ImportError: فشل استيراد وحدة.IOError: خطأ في الإدخال/الإخراج (مثلًا عند قراءة ملف).AttributeError: محاولة الوصول إلى خاصية غير موجودة.
بنية try-except-else-finally
تستخدم بايثون الكتل try وexcept وelse وfinally لمعالجة الاستثناءات.
البنية الأساسية try-except
try:
# شيفرة قد تُولّد استثناءً
resultat = 10 / 0
except ZeroDivisionError:
# شيفرة تُنفَّذ إذا وقع ZeroDivisionError
print("Erreur : Division par zéro !")
في هذا المثال، عندما يُولّد 10 / 0 الاستثناء ZeroDivisionError، تُنفَّذ كتلة except المقابلة له.
التقاط عدة أنواع من الاستثناءات
يمكنك التقاط أنواع مختلفة من الاستثناءات بمعالِجات منفصلة:
try:
# شيفرة قد تُولّد استثناءات
nombre = int(input("Entrez un nombre : "))
resultat = 10 / nombre
except ValueError:
# تُنفَّذ إذا لم يكن المُدخَل رقمًا صالحًا
print("Erreur : Veuillez entrer un nombre valide.")
except ZeroDivisionError:
# تُنفَّذ إذا كان nombre == 0
print("Erreur : La division par zéro n'est pas autorisée.")
يمكنك أيضًا التقاط عدة استثناءات بالمعالِج نفسه:
try:
# شيفرة قد تُولّد استثناءات
nombre = int(input("Entrez un nombre : "))
resultat = 10 / nombre
except (ValueError, ZeroDivisionError):
# تُنفَّذ إذا وقع أحد الاستثناءات المذكورة
print("Erreur : Entrée invalide ou division par zéro.")
التقاط جميع الاستثناءات
على الرغم من أن ذلك غير مستحسن عمومًا (لأنه قد يُخفي أخطاءً غير متوقعة)، يمكنك التقاط جميع الاستثناءات:
try:
# شيفرة قد تُولّد أي استثناء
# ...
except Exception as e:
# تُنفَّذ لأي استثناء يرث من Exception
print(f"Une erreur s'est produite : {e}")
كتلة else
تُنفَّذ كتلة else إذا لم يُطلَق أي استثناء في كتلة try:
try:
nombre = int(input("Entrez un nombre : "))
resultat = 10 / nombre
except ValueError:
print("Entrée invalide.")
except ZeroDivisionError:
print("Division par zéro.")
else:
# تُنفَّذ هذه الكتلة إذا لم يُطلَق أي استثناء
print(f"Le résultat est {resultat}")
كتلة finally
تُنفَّذ كتلة finally مهما حدث، سواء أُطلق استثناء أم لا:
try:
fichier = open("donnees.txt", "r")
# عمليات على الملف
except FileNotFoundError:
print("Le fichier n'existe pas.")
finally:
# تُنفَّذ هذه الكتلة دائمًا
# مثالية لتحرير الموارد
if 'fichier' in locals():
fichier.close()
print("Fichier fermé.")
تفيد كتلة finally بشكل خاص في التأكد من تحرير الموارد بشكل صحيح، حتى في حالة حدوث خطأ.
إطلاق الاستثناءات
يمكنك إطلاق الاستثناءات (أو “throw”) عمدًا باستخدام التعليمة raise:
def diviser(a, b):
if b == 0:
raise ZeroDivisionError("Division par zéro non autorisée")
return a / b
try:
resultat = diviser(10, 0)
except ZeroDivisionError as e:
print(f"Erreur : {e}")
إنشاء استثناءات مخصّصة
يمكنك إنشاء أنواع الاستثناءات الخاصة بك بالوراثة من الصنف Exception أو من أحد أصنافه الفرعية:
class ValeurNegativeError(Exception):
"""Exception levée lorsqu'une valeur négative est utilisée alors qu'elle ne devrait pas l'être."""
pass
def calculer_racine_carree(nombre):
if nombre < 0:
raise ValeurNegativeError("La racine carrée d'un nombre négatif n'est pas définie pour les nombres réels.")
import math
return math.sqrt(nombre)
try:
resultat = calculer_racine_carree(-1)
except ValeurNegativeError as e:
print(f"Erreur : {e}")
التأكيدات (Assertions)
التأكيدات هي فحوصات لتصحيح الأخطاء تختبر ما إذا كان شرط ما صحيحًا. إذا كان الشرط خاطئًا، يُطلَق الاستثناء AssertionError:
def diviser(a, b):
assert b != 0, "Le diviseur ne peut pas être zéro"
return a / b
try:
resultat = diviser(10, 0)
except AssertionError as e:
print(f"Assertion échouée : {e}")
تُستخدم التأكيدات أساسًا لتصحيح الأخطاء والاختبارات، ويمكن تعطيلها في وضع الإنتاج.
باستخدام التعليمة with
تُستخدم التعليمة with لضمان إدارة الموارد بشكل صحيح، حتى في حالة وقوع استثناء. وهي وسيلة أنظف لمعالجة الاستثناءات في العمليات التي تتطلب موارد مثل الملفات:
try:
with open("fichier.txt", "r") as fichier:
contenu = fichier.read()
# يُغلَق الملف تلقائيًا في نهاية كتلة with،
# حتى لو أُطلق استثناء
except FileNotFoundError:
print("Le fichier n'existe pas.")
التسلسل الهرمي للاستثناءات
تشكّل الاستثناءات في بايثون تسلسلًا هرميًا. إليك جزءًا مبسّطًا من هذا التسلسل:
BaseExceptionExceptionArithmeticErrorZeroDivisionErrorOverflowError
LookupErrorIndexErrorKeyError
OSErrorFileNotFoundErrorPermissionError
TypeErrorValueError- …
SystemExitKeyboardInterrupt- …
عند التقاط الاستثناءات، يكون الترتيب مهمًا. إذا كانت لديك عدة كتل except، فإن بايثون تفحصها بالترتيب. وإذا كان استثناء ما صنفًا فرعيًا لاستثناء آخر سبق أن التقطته، فلن يُبلَغ إليه أبدًا:
try:
# شيفرة قد تُولّد استثناءات
# ...
except Exception:
# ستلتقط هذه الكتلة جميع الاستثناءات التي ترث من Exception
print("Une exception s'est produite")
except ZeroDivisionError:
# لن تُبلَغ هذه الكتلة أبدًا لأن ZeroDivisionError صنف فرعي من Exception
print("Division par zéro")
لتجنّب هذه المشكلة، ضع الاستثناءات الأكثر تحديدًا قبل الأكثر عمومية.
أفضل الممارسات في معالجة الاستثناءات
-
كن محدِّدًا: التقط الاستثناءات المحددة بدلًا من استخدام
exceptعامة. -
لا تُخفِ الأخطاء بصمت: تجنّب التقاط استثناء دون معالجته معالجة سليمة أو دون ترك أثر له.
-
استخدم
finallyللتنظيف: استخدم كتلةfinallyللتأكد من تحرير الموارد بشكل صحيح. -
أنشئ استثناءات مخصّصة عند الاقتضاء: يمكن أن يجعل ذلك شيفرتك أكثر قابلية للقراءة ويتيح معالجة أدق للأخطاء.
-
أبقِ كتل try صغيرة: لا تضع في كتلة
tryإلا الشيفرة التي قد تُولّد الاستثناء. -
سجّل الاستثناءات: في تطبيقات الإنتاج، فكّر في تسجيل الاستثناءات في ملفات السجل (log).
الخلاصة
تُعدّ المعالجة الفعّالة للاستثناءات مهارة أساسية لكل مبرمج بايثون. فهي تتيح لك كتابة برامج متينة قادرة على التعامل مع الأخطاء بأسلوب أنيق.
في هذا الدرس، استكشفنا كيفية تحديد الاستثناءات والتقاطها وإطلاقها، وكيفية إنشاء أنواع الاستثناءات الخاصة بك، وكيفية استخدام التعليمة with لإدارة الموارد. كما ناقشنا أفضل الممارسات لمعالجة فعّالة للاستثناءات.
في الدرس القادم، سنتناول التعامل مع الملفات في بايثون، وهو مجال تكتسي فيه معالجة الاستثناءات أهمية خاصة.
التعليقات
التعليقات مدعومة عبر GitHub Discussions
سجّل الدخول باستخدام GitHub للمشاركة في النقاش