معالجة الاستثناءات
تحدث دائماً مواقف غير متوقعة أثناء تنفيذ البرنامج — ملفات غير موجودة، انقطاع الشبكة، إدخال المستخدم لبيانات غير صالحة... بدون معالجة هذه "الاستثناءات"، ينهار البرنامج. معالجة الاستثناءات تسمح لبرنامجك بالتعافي من الأخطاء وتقديم رسائل سهلة للمستخدم بدلاً من رسائل الخطأ المشفرة.
1. ما هو الاستثناء
الاستثناء هو خطأ يحدث أثناء تنفيذ البرنامج. إذا لم يتم معالجة الاستثناء، ينهي البرنامج تشغيله:
# هذا الكود سينهار
# print(10 / 0) # ZeroDivisionError: division by zero
# int("abc") # ValueError: invalid literal for int()
# open("non_existent.txt") # FileNotFoundError
استخدم try-except لمعالجة هذه الأخطاء بشكل أنيق:
مثال: try-except لمعالجة القسمة وأخطاء الإدخال
try:
number = int(input("Enter a number: "))
result = 10 / number
print(f"10 / {number} = {result}")
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("Cannot divide by zero!")
تشغيل نموذجي:
Enter a number: abc
That's not a valid number!
2. هيكل try-except الكامل
مثال: هيكل try-except-else-finally الكامل
try:
# كود قد يسبب خطأ
file = open("data.txt", "r", encoding="utf-8")
content = file.read()
number = int(content)
print(f"The number is: {number}")
except FileNotFoundError:
print("File not found!")
except ValueError:
print("File content is not a valid number!")
else:
# يُنفّذ عندما لا يحدث استثناء
print("Successfully read and converted!")
finally:
# يُنفّذ دائماً، بغض النظر عن الاستثناءات
print("Cleanup: closing file")
try:
file.close()
except NameError:
pass
| الجزء | متى يُنفّذ | الغرض |
|---|---|---|
try |
دائماً | ضع الكود الذي قد يسبب خطأ هنا |
except |
عند حدوث الاستثناء المحدد | تعامل مع الخطأ |
else |
عندما لا يحدث استثناء | منطق بعد النجاح |
finally |
دائماً (مع أو بدون استثناء) | تنظيف الموارد (إغلاق الملفات/تحرير الاتصالات) |
else يُستخدم أقل، بينما finally يُستخدم بكثرة. جملة with (مدير السياق) تتعامل مع تحرير الموارد تلقائياً ويمكنها غالباً استبدال finally.
3. أنواع الاستثناءات الشائعة
# ZeroDivisionError — القسمة على صفر
# int("abc") # ValueError — قيمة غير صالحة
# [1, 2][5] # IndexError — فهرس خارج النطاق
# {"a": 1}["b"] # KeyError — مفتاح غير موجود
# open("x.txt") # FileNotFoundError — ملف غير موجود
# 10 + "hello" # TypeError — خطأ في النوع
# import nonexistent # ModuleNotFoundError — وحدة غير موجودة
التقاط جميع الاستثناءات (غير موصى به)
مثال: الطريقة الصحيحة مقابل الطريقة الخاطئة لالتقاط الاستثناءات
try:
# بعض الكود
pass
except: # يلتقط جميع الاستثناءات
print("Something went wrong")
try:
pass
except Exception as e: # يلتقط الاستثناءات المعروفة
print(f"Error: {e}")
except: بمفرده غير موصى به — يلتقط استثناءات النظام مثل KeyboardInterrupt (Ctrl+C) و SystemExit، مما يمنع إنهاء البرنامج بشكل طبيعي. استخدم except Exception as e بدلاً من ذلك. أفضل ممارسة هي التقاط أنواع الاستثناءات المحددة التي تتوقعها فقط.
4. raise: إثارة الاستثناءات صراحة
أحياناً تحتاج لإخبار المتصل "حدث خطأ ما." استخدم raise:
مثال: raise لإثارة استثناء
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(f"Error: {e}") # Error: Cannot divide by zero!
مثال: raise للتحقق من كلمة المرور
def validate_password(password):
"""تحقق من قوة كلمة المرور؛ أثر استثناءً إذا لم تتحقق الشروط"""
if len(password) < 6:
raise ValueError("Password must be at least 6 characters")
if not any(c.isdigit() for c in password):
raise ValueError("Password must contain a digit")
if not any(c.isalpha() for c in password):
raise ValueError("Password must contain a letter")
return True
try:
validate_password("123")
except ValueError as e:
print(f"Invalid password: {e}")
raise؟ عندما تواجه الدالة موقفاً "لا ينبغي أن يحدث" — قيم معاملات غير صالحة، شروط مسبقة غير محققة، تبعيات غير متوفرة. أثر استثناءً ليقرر المتصل كيفية التعامل معه، بدلاً من ابتلاع الخطأ بصمت داخل الدالة.
5. فئات الاستثناء المخصصة
يمكنك إنشاء أنواع الاستثناءات الخاصة بك للحصول على معلومات خطأ أكثر دقة:
مثال: استثناءات مخصصة للسحب البنكي
class BalanceError(Exception):
"""استثناء رصيد غير كافٍ"""
pass
class AccountLockedError(Exception):
"""استثناء حساب مقفل"""
pass
def withdraw(balance, amount):
if amount > balance:
raise BalanceError(f"Insufficient balance! Need {amount}, have {balance}")
if balance < 0:
raise AccountLockedError("Account is locked")
return balance - amount
try:
withdraw(100, 200)
except BalanceError as e:
print(f"Transaction failed: {e}")
except AccountLockedError as e:
print(f"Transaction failed: {e}")
Exception؛ عادة اسم فئة فقط كافٍ، لا حاجة لطرق إضافية.
6. logging: تسجيل الأخطاء باحترافية
استخدام print() لرسائل الخطأ يعمل في البرامج النصية البسيطة، لكن في المشاريع الإنتاجية، استخدم وحدة logging:
مثال: تهيئة واستخدام logging
import logging
# تهيئة أساسية
logging.basicConfig(
level=logging.INFO, # مستوى التسجيل
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("app.log", encoding="utf-8"), # اكتب إلى ملف
logging.StreamHandler() # أيضاً أخرج إلى وحدة التحكم
]
)
# مستويات تسجيل مختلفة
logging.debug("Debug info") # تصحيح، عادة لا يُخرج
logging.info("Application started successfully") # معلومات عامة
logging.warning("Disk usage at 85%") # تحذير
logging.error("Database connection failed") # خطأ
logging.critical("System crash!") # خطأ فادح
المخرجات (تُكتب إلى كل من الملف ووحدة التحكم):
2026-06-23 16:00:00,123 [INFO] Application started successfully
2026-06-23 16:00:00,124 [WARNING] Disk usage at 85%
2026-06-23 16:00:00,124 [ERROR] Database connection failed
logging على print(): 1) يمكن الإخراج إلى كل من وحدة التحكم والملف في وقت واحد. 2) لديه تحكم في المستوى (أظهر كل شيء أثناء التطوير، فقط WARNING+ في الإنتاج). 3) يتضمن تلقائياً الطوابع الزمنية. 4) لا حاجة لحذف مجموعة من جمل print() قبل الإصدار.
حالات الاستخدام الشائعة
- التحقق من صحة إدخال المستخدم: استخدم
try-exceptلمعالجة أخطاء تحويلint()، مع تقديم رسائل سهلة للمستخدم. - عمليات الملفات: استخدم
try-exceptلمعالجة أخطاء عدم وجود الملف، رفض الإذن، وغيرها. - طلبات الشبكة: تعامل مع انتهاء المهلة، فشل الاتصال، وأخطاء تحليل البيانات.
- عمليات قاعدة البيانات: تعامل مع فشل الاتصال، أخطاء بناء جملة SQL، وانتهاكات القيود الفريدة.
- تطوير API: استخدم الاستثناءات المخصصة للتمييز بدقة بين أنواع الأخطاء المختلفة.
❓ أسئلة شائعة
as e في except Exception as e؟as e يُسند كائن الاستثناء الملتقط إلى المتغير e، مما يسمح لك بفحص تفاصيل الاستثناء — print(e) يُظهر وصف الخطأ، type(e).__name__ يُظهر اسم نوع الاستثناء. بدون as e، لا يزال بإمكانك التقاط الاستثناء لكن لا يمكنك رؤية معلومات الخطأ التفصيلية.
س:متى يجب استخدام الاستثناءات مقابل فحوصات if؟ ج:استخدم if للأخطاء التي يمكنك توقعها مسبقاً (مثل التحقق من القسمة على صفر). استخدم try-except للأخطاء التي لا يمكنك توقعها (مثل طلبات الشبكة، عمليات الملفات). أسلوب EAFP في مجتمع بايثون يميل نحو استخدام try — لكن هذه مسألة ذوق، وكلا النهجين لهما حالات استخدامهما.
س:هل الكود في finally يُنفّذ دائماً؟ ج:دائماً تقريباً — حتى إذا كانت كتلة try تحتوي على return أو break أو continue، لا يزال finally يُنفّذ. الاستثناء الوحيد هو عندما يُنهي البرنامج قسراً (مثل os._exit() أو انقطاع الكهرباء). هذا يجعل finally مكاناً ممتازاً لتنظيف الموارد.📖 ملخص
- الاستثناءات هي أخطاء وقت التشغيل؛ تعامل معها باستخدام
try-except - الهيكل:
try←except(حسب نوع الاستثناء المحدد) ←else(لا استثناء) ←finally(يُنفّذ دائماً) - الاستثناءات الشائعة:
ValueError,TypeError,FileNotFoundError,ZeroDivisionError,IndexError,KeyError raiseيثير الاستثناءات صراحة، تاركاً للمتصل معالجة الأخطاء- فئات الاستثناء المخصصة ترث من
Exception، تميز بدقة أنواع الأخطاء - وحدة
loggingتحل محلprint()، تدعم التحكم في المستوى، الإخراج إلى ملف، والطوابع الزمنية
📝 تمارين
-
مبتدئ (الصعوبة ⭐): اكتب برنامجاً يطلب من المستخدم رقمين ويحسب قسمتهما. استخدم
try-exceptلمعالجةValueError(الإدخال ليس رقماً) وZeroDivisionError(القسمة على صفر)، مع رسالة خطأ ودية لكل منهما. -
متوسط (الصعوبة ⭐⭐): اكتب دالة
read_int_from_file(filename)تقرأ الرقم الأول من ملف وتُرجعه. استخدم try-except لمعالجة: ملف غير موجود، محتوى الملف ليس رقماً صالحاً، ملف فارغ. أعد رسالة خطأ مختلفة لكل حالة. -
متقدم (الصعوبة ⭐⭐⭐): اكتب برنامج "آلة حاسبة آمنة." يُدخل المستخدم تعبيراً (مثل
10 / 3)، ويحسب البرنامج ويُخرج النتيجة. المتطلبات: 1) استخدمtry-exceptلمعالجة القسمة على صفر، أخطاء القيمة، وأخطاء النوع. 2) استخدمloggingلتسجيل كل عملية حساب (INFO) وخطأ (ERROR) إلى ملفcalculator.log. 3) ادعمexitللخروج. تلميح: استخدمeval()لتقييم التعبير (لاحظ مخاوف الأمان — مقبول لهذا التمرين).



