شرح try و except في Python: كيفية التعامل مع الاستثناءات باحتراف
ما المقصود بالاستثناءات في Python؟
عند تطوير البرامج باستخدام Python، قد يعمل الكود بشكل صحيح من الناحية النحوية والمنطقية، لكنه يتوقف أثناء التشغيل بسبب حالات غير متوقعة مثل إدخال غير صالح، أو محاولة الوصول إلى عنصر غير موجود، أو فتح ملف غير متاح. هذه الحالات تُسمّى Exceptions، ويُعد التعامل معها جزءاً أساسياً من كتابة تطبيقات مستقرة وسهلة الصيانة.
توفر Python آلية واضحة لمعالجة هذه المشكلات عبر الكتل try وexcept، بحيث يمكن للتطبيق الاستمرار أو إنهاء التنفيذ بصورة منظمة بدلاً من الانهيار المفاجئ.

صيغة try و except في Python
قبل استعراض الأمثلة العملية، من المهم فهم البنية العامة لاستخدام try وexcept في Python:
try:
# There can be errors in this block
except <error type>:
# Do this to handle exception;
# executed if the try block throws an error
else:
# Do this if try block executes successfully without errors
finally:
# This block is always executed
وظيفة كل كتلة برمجية
try: تحتوي على الكود الذي قد يُنتج خطأ أثناء التنفيذ.except: تُنفّذ عند وقوع استثناء محدد داخل كتلةtry.else: تُنفّذ فقط إذا انتهتtryبنجاح ومن دون أي خطأ.finally: تُنفّذ دائماً سواء حدث خطأ أم لا، وتُستخدم غالباً لتحرير الموارد مثل إغلاق الملفات أو الاتصالات.
من الأفضل دائماً تحديد نوع الخطأ داخل except بدقة، لأن استخدام except بشكل عام من دون تحديد الاستثناء قد يُخفي أخطاء منطقية مهمة ويصعّب عملية التصحيح لاحقاً.
هل يمكن معالجة أكثر من خطأ؟
نعم، في كثير من السيناريوهات قد يحتوي الكود داخل try على أكثر من عملية قد تفشل. على سبيل المثال، قد تتعامل في الوقت نفسه مع قائمة list، وقاموس dictionary، وملف خارجي. في هذه الحالة يمكنك إضافة أكثر من كتلة except، بحيث تعالج كل نوع خطأ بالطريقة المناسبة له.
كيفية التعامل مع ZeroDivisionError في Python
من أكثر الأخطاء شيوعاً في Python محاولة القسمة على الصفر. لنأخذ الدالة التالية:
def divide(num, div):
return num/div
عند تمرير قيم صحيحة، ستعمل الدالة كما هو متوقع:
res = divide(100, 8)
print(res) # Output 12.5
res = divide(568, 64)
print(res) # Output 8.875
لكن عند محاولة القسمة على 0 سيتوقف البرنامج بسبب ZeroDivisionError:
divide(27, 0)
# Output
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-19-932ea024ce43> in <module>()
----> 1 divide(27, 0)
<ipython-input-1-c98670fd7a12> in divide(num, div)
1 def divide(num,div):
----> 2 return num/div
ZeroDivisionError: division by zero
الحل هو وضع الاستدعاء داخل try ومعالجة الخطأ داخل except:
try:
res = divide(num, div)
print(res)
except ZeroDivisionError:
print("You tried to divide by zero :(")
وعند إدخال قيم صحيحة سيستمر التنفيذ بشكل طبيعي:
divide(10, 2)
# Output 5.0
أما عند القسمة على الصفر فستظهر رسالة واضحة بدلاً من انهيار التطبيق:
divide(10, 0)
# Output You tried to divide by zero :(
كيفية التعامل مع TypeError في Python
يحدث TypeError عندما تُجرى عملية على نوع بيانات غير مناسب. تأمل المثال التالي:
def add_10(num):
return num + 10
إذا مرّرت رقماً، فسيكون الناتج صحيحاً:
result = add_10(89)
print(result) #Output 99
لكن إذا مرّرت نصاً مثل "five" بدلاً من قيمة رقمية:
add_10("five")
فسيظهر الخطأ التالي:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-9844e949c84e> in <module>()
----> 1 add_10("five")
<ipython-input-13-2e506d74d919> in add_10(num)
1 def add_10(num):
----> 2 return num + 10
TypeError: can only concatenate str (not "int") to str
يمكنك معالجة هذا السيناريو على النحو التالي:
my_num = "five"
try:
result = add_10(my_num)
print(result)
except TypeError:
print("The argument `num` should be a number")
وبذلك يحصل المستخدم على رسالة مفهومة توضّح أن نوع البيانات المُدخل غير مناسب:
The argument `num` should be a number
كيفية التعامل مع IndexError في Python
يظهر IndexError عند محاولة الوصول إلى عنصر داخل list أو أي كائن قابل للفهرسة باستخدام موقع غير موجود.
لنفترض أن لدينا القائمة التالية:
my_list = ["Python", "C", "C++", "JavaScript"]
print(my_list[2]) #Output C++
في هذا المثال، الفهرس 2 صالح، لذلك ستتم طباعة C++. لكن عند محاولة الوصول إلى فهرس خارج النطاق:
print(my_list[4])
سيظهر الخطأ:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-7-437bc6501dea> in <module>()
1 my_list = ["Python", "C", "C++", "JavaScript"]
----> 2 print(my_list[4])
IndexError: list index out of range
يمكن معالجة الخطأ باستخدام try وexcept بالشكل التالي:
search_idx = 3
try:
print(my_list[search_idx])
except IndexError:
print("Sorry, the list index is out of range")
إذا كانت قيمة search_idx صحيحة، فسيُطبع العنصر المطلوب:
JavaScript
أما إذا كانت خارج المجال المسموح:
search_idx = 4
try:
print(my_list[search_idx])
except IndexError:
print("Sorry, the list index is out of range")
فستظهر رسالة مخصصة بدلاً من رسالة الخطأ الطويلة:
Sorry, the list index is out of range
كيفية التعامل مع KeyError في Python
عند العمل مع dictionary في Python، يظهر KeyError عندما تحاول الوصول إلى مفتاح غير موجود.
my_dict = {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
search_key = "non-existent key"
print(my_dict[search_key])
بما أن المفتاح المطلوب غير موجود داخل القاموس، فستحصل على الخطأ التالي:
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-2-2a61d404be04> in <module>()
1 my_dict = {"key1": "value1", "key2": "value2", "key3": "value3"}
2 search_key = "non-existent key"
----> 3 my_dict[search_key]
KeyError: 'non-existent key'
يمكن التعامل معه بسهولة كالتالي:
try:
print(my_dict[search_key])
except KeyError:
print("Sorry, that's not a valid key!")
Sorry, that's not a valid key!
وإذا رغبت في عرض اسم المفتاح غير الصحيح لمساعدة المستخدم على اكتشاف الخطأ الإملائي أو المنطقي، فاستخدم الصيغة التالية:
try:
print(my_dict[search_key])
except KeyError as error_msg:
print(f"Sorry, {error_msg} is not a valid key!")
وسيكون الناتج:
Sorry, 'non-existent key' is not a valid key!
كيفية التعامل مع FileNotFoundError في Python
يُعد FileNotFoundError من الأخطاء الشائعة عند التعامل مع الملفات، ويحدث عندما تحاول فتح ملف غير موجود في المسار المحدد.
في المثال التالي نحاول فتح ملف وقراءة محتواه:
my_file = open("/content/sample_data/my_file.txt")
contents = my_file.read()
print(contents)
إذا لم يكن الملف موجوداً، فستظهر الرسالة التالية:
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-4-4873cac1b11a> in <module>()
----> 1 my_file = open("my_file.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'my_file.txt'
الأسلوب الاحترافي هنا هو الجمع بين try وexcept وelse وfinally:
try:
my_file = open("/content/sample_data/my_file.txt")
except FileNotFoundError:
print(f"Sorry, the file does not exist")
else:
contents = my_file.read()
print(contents)
finally:
my_file.close()
هذا النمط يوضح عدة نقاط مهمة:
- داخل
tryنحاول فتح الملف. - داخل
exceptنعالج غياب الملف برسالة واضحة. - داخل
elseنقرأ المحتوى فقط إذا نجح فتح الملف. - داخل
finallyنغلق الملف لتحرير الموارد.
عند غياب الملف، سينتهي البرنامج بسلاسة مع الرسالة التالية:
Sorry, the file does not exist
وفي حال كان الملف موجوداً، سيتم تنفيذ else وقراءة محتواه كما هو متوقع.



أفضل ممارسات استخدام try و except في Python
1) حدّد نوع الاستثناء بدقة
استخدام except Exception أو except بشكل عام قد يكون مناسباً في حالات محدودة، لكنه ليس الخيار الأفضل في أغلب المشاريع. كلما حدّدت نوع الخطأ بدقة، كان الكود أوضح وأسهل في التتبع.
2) اجعل كتلة try صغيرة قدر الإمكان
من الأفضل ألا تضع كمية كبيرة من التعليمات داخل try. ضع فقط الأسطر التي تتوقع فعلاً أنها قد تُسبب الاستثناء، حتى تعرف مصدر الخطأ بسرعة.
3) استخدم else للعمليات التابعة للنجاح
إذا كنت تريد تنفيذ خطوة إضافية فقط بعد نجاح العملية الأساسية، فإن else تمنحك تنظيماً أفضل للكود وتفصل بين منطق التنفيذ ومنطق المعالجة.
4) لا تتجاهل الأخطاء بصمت
معالجة الاستثناء لا تعني إخفاءه تماماً. ينبغي إظهار رسالة مفيدة أو تسجيل الخطأ في السجلات logs إذا كان التطبيق أكبر حجماً.
5) استخدم finally عند الحاجة إلى تنظيف الموارد
إذا كنت تتعامل مع ملفات، أو اتصالات شبكية، أو جلسات قاعدة بيانات، فإن finally مهمة لضمان الإغلاق السليم مهما كانت نتيجة التنفيذ.
ملخص سريع لأشهر الاستثناءات المذكورة
| نوع الاستثناء | متى يحدث؟ | مثال شائع |
|---|---|---|
ZeroDivisionError |
عند القسمة على صفر | 10 / 0 |
TypeError |
عند استخدام نوع بيانات غير مناسب | جمع str مع int |
IndexError |
عند الوصول إلى فهرس غير موجود | my_list[10] |
KeyError |
عند طلب مفتاح غير موجود في dictionary |
my_dict["x"] |
FileNotFoundError |
عند محاولة فتح ملف غير موجود | open("missing.txt") |
لماذا يُعد التعامل مع الاستثناءات مهماً في تطوير البرمجيات؟
معالجة الاستثناءات ليست مجرد وسيلة لتجنب توقف البرنامج، بل هي عنصر أساسي في بناء تجربة استخدام أفضل. عندما يتلقى المستخدم رسالة واضحة ومفهومة بدلاً من تتبّع أخطاء طويل ومعقد، يصبح التطبيق أكثر احترافية. كذلك تساعد هذه الممارسة المطورين على كتابة كود أكثر موثوقية وقابلية للتوسع.
الخلاصة التقنية
استخدام try وexcept في Python يمنحك طريقة عملية وآمنة للتعامل مع أخطاء التشغيل المتوقعة من دون إفساد تدفق البرنامج. والأفضل دائماً هو معالجة الاستثناءات المحددة مثل ZeroDivisionError وTypeError وFileNotFoundError بدلاً من التقاط كل شيء بشكل عام. كلما كان التعامل مع الأخطاء أكثر دقة وتنظيماً، كان الكود أسهل في الصيانة وأكثر جاهزية للاستخدام في التطبيقات الحقيقية.