Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

ما هي معالجة الاستثناءات (Exception Handling)؟

معالجة الاستثناءات (exception handling) طريقة يستجيب بها البرنامج للأخطاء التي تقع أثناء تشغيله. يوضع الكود الذي قد يفشل في كتلة محمية مثل try، وعند رفع خطأ ينتقل التنفيذ إلى معالج مثل except أو catch بدل أن يتعطل البرنامج.

بقلم Kevin Spektor, شريك مؤسس ورئيس قسم التكنولوجيا (CTO)

آخر تحديث 24 سبتمبر 2026

البرنامج الذي يسأل الناس عن أعمارهم سيتلقى عاجلًا أو آجلًا "twenty" بدل 20. تحويل ذلك النص خطأ وقت تشغيل: int("twenty") ترفع ValueError، وإن لم تكن هناك خطة لذلك يتوقف البرنامج. معالجة الاستثناءات هي تلك الخطة. تحدد الكود الذي قد يفشل، وتكتب الكود الذي يُنفَّذ إن فشل.

Next year you will be 35
Not a number: twenty
Next year you will be 20
Done

يُبلَّغ عن الإجابة الخاطئة، وتتابع الحلقة مع الإجابة التالية. لولا try لطبع البرنامج سطرًا واحدًا ثم traceback.

كيف تعمل معالجة الاستثناءات

  1. يُنفَّذ الكود داخل try بشكل عادي.
  2. تفشل عملية ما، فترفع بيئة التشغيل استثناءً: كائنًا له نوع (ValueError) ورسالة (invalid literal for int() with base 10: 'twenty').
  3. تتوقف كتلة try عند ذلك السطر. تُتخطى أسطرها المتبقية، ولهذا لا تُطبع عبارة "Next year you will be" مع "twenty".
  4. تقارن بيئة التشغيل نوع الاستثناء بكل عبارة except من الأعلى إلى الأسفل، وتنفّذ أول عبارة تطابقه.
  5. بعد انتهاء المعالج، يتابع البرنامج من أول سطر بعد عبارة try كاملة.
  6. إن لم تطابقه أي عبارة، يغادر الاستثناء الدالة الحالية ويستمر البحث في الدالة التي استدعتها، ثم في الدالة التي استدعت تلك. يسمى هذا فكّ مكدس الاستدعاءات (unwinding). وإن وصل البحث إلى قمة البرنامج دون تطابق، ينتهي البرنامج بـ traceback.

الخطوة 6 تعني أن المعالج لا يلزم أن يكون في الدالة التي وقع فيها الخطأ. هنا يقع الفشل على عمق استدعاءين، والمعالج في الأسفل يلتقطه مع ذلك:

parsing 4.50
parsing free
Could not add the prices: could not convert string to float: 'free'

فشلت float("free") داخل parse_price. لم يكن لدى parse_price ولا total معالج، فتُركت الدالتان كلتاهما، ولم يُحلَّل "3.20" أبدًا. هذا هو قرار التصميم الأساسي الذي تمنحك إياه معالجة الاستثناءات: عالج الخطأ حيث تعرف ما يجب فعله حياله، وغالبًا ما يكون ذلك بعيدًا عن موضع وقوعه.

try و except و else و finally

لعبارة try في بايثون جزءان اختياريان آخران. تُنفَّذ else فقط حين تنتهي كتلة try دون استثناء. وتُنفَّذ finally في كل الحالات: بعد النجاح، وبعد استثناء عولج، وحتى حين تعود الدالة من داخل try.

no error
finally runs either way
2.0
b was zero
finally runs either way
None

finally هي مكان التنظيف: إغلاق ملف، أو تحرير قفل، أو إغلاق اتصال بقاعدة البيانات. في بايثون تقوم عبارة with بهذا التنظيف للملفات تلقائيًا، وهي الخيار المعتاد لها.

رفع استثناءاتك الخاصة

الاستثناءات ليست فقط للأخطاء التي تكتشفها اللغة. عندما تتلقى دالتك مدخلًا لا تستطيع العمل به، يمكنها رفع استثناء لترفضه، ويقرر من استدعاها ما يحدث بعد ذلك. وتعريف صنف استثناء صغير يعطي الخطأ اسمًا يصفه.

70
Refused: cannot take 250, balance is 100

تستخدم بايثون raise. وتستخدم جافا و C++ وجافا سكريبت throw للغرض نفسه.

معالجة الاستثناءات في لغات أخرى

اللغةالكلمات المحجوزةما يستحق المعرفة
بايثونtry وexcept وelse وfinally وraiseكل استثناء كائن يرث من BaseException
جافاtry وcatch وfinally وthrow وthrowsالاستثناءات المُتحقَّق منها مثل IOException يجب التقاطها أو إعلانها بـ throws
C++try وcatch وthrowلا توجد finally، فالهدّامات تنظّف أثناء فكّ المكدس
جافا سكريبتtry وcatch وfinally وthrowالأخطاء في كود async تُلتقط بوضع try حول await
Cلا شيءالدوال تُرجع رمز خطأ، وكثير منها يضبط errno
Goلا شيء للأخطاء العاديةالدوال تُرجع قيمة error يفحصها المستدعي

مثال التحليل نفسه في جافا يبدو هكذا:

try {
    int age = Integer.parseInt("twenty");
    System.out.println(age + 1);
} catch (NumberFormatException e) {
    System.out.println("Not a number: " + e.getMessage());
} finally {
    System.out.println("done");
}

تقسم جافا الاستثناءات إلى مجموعتين. الاستثناءات المُتحقَّق منها (checked)، مثل IOException، تصف مشاكل خارج سيطرة البرنامج، ويرفض المترجم بناء كود لا يلتقطها ولا يُعلنها. أما الاستثناءات غير المُتحقَّق منها (unchecked)، وهي الأصناف الفرعية من RuntimeException مثل NullPointerException، فتعني عادةً وجود خلل برمجي، ولا يُلزمك المترجم بمعالجتها.

أخطاء شائعة

  • التقاط كل شيء. عبارة except: المجردة في بايثون تلتقط أيضًا KeyboardInterrupt، فيتوقف Ctrl+C عن العمل، وexcept Exception: تلتقط أخطاء برمجية لم تخطط لها قط. التقط النوع المحدد الذي تعرف كيف تعالجه.
  • ابتلاع الخطأ. except ValueError: pass تُخفي المشكلة. اطبع ما حدث أو سجّله على الأقل.
  • كتلة try كبيرة جدًا. غلّف فقط الأسطر التي قد ترفع الاستثناء الذي تلتقطه، حتى لا يُعالَج خطأ لا علاقة له عن طريق الخطأ.
  • التقاط الأخطاء البرمجية بدل إصلاحها. خطأ IndexError من حلقة تتقدم خطوة زائدة هو خلل في الكود. ومعالجته تُبقي الكود الخاطئ حيًا.
  • توقُّع أن تمنع catch تعطل برنامج C أو C++. خطأ التجزئة إشارة من نظام التشغيل وليس استثناءً، وفي لينكس و macOS لا تراه أبدًا عبارة catch (...) في C++.

إلى أين بعد ذلك

الأخطاء التي تستجيب لها معالجة الاستثناءات موصوفة في صفحة خطأ وقت التشغيل، والتعطل الذي لا تستطيع التقاطه في صفحة خطأ التجزئة. وللصياغة الكاملة، اقرأ أدلة الاستثناءات في بايثون وtry و catch في جافا والاستثناءات في C++ وtry و catch في جافا سكريبت. وتتدرب عليها دورة جافا ودورة بايثون كلتاهما بتمارين حقيقية.

الأسئلة الشائعة

ما هي الكلمات المحجوزة الخمس لمعالجة الاستثناءات في جافا؟
try وcatch وfinally وthrow وthrows. تحدد try الكود المراد مراقبته، وتعالج catch نوعًا محددًا من الاستثناءات، وتنفّذ finally كود التنظيف سواء وقع استثناء أم لا. أما throw فترفع استثناءً، وthrows في توقيع الدالة تُعلن الاستثناءات المُتحقَّق منها التي قد تمررها الدالة إلى من استدعاها.
كيف تعمل معالجة الاستثناءات في C++؟
تستخدم C++ الكلمات try وcatch وthrow. يمكن رمي أي نوع، لكن الاصطلاح أن ترمي كائنًا مشتقًا من std::exception، مثل std::runtime_error، وأن تلتقطه بالمرجع: catch (const std::exception& e). لا توجد finally في C++، فالتنظيف يتم في الهدّامات (destructors) التي تُنفَّذ تلقائيًا أثناء فكّ المكدس.
ما الفرق بين الخطأ والاستثناء؟
في الاستخدام اليومي، الخطأ هو أي شيء يسوء، والاستثناء هو الكائن الذي تستخدمه اللغة للإبلاغ عنه. تضع جافا حدًا أوضح: Exception تشمل المشاكل التي يمكن للبرنامج أن يعالجها بشكل معقول، بينما Error، مثل OutOfMemoryError أو StackOverflowError، تشير إلى عطل في JVM لا ينبغي للبرنامج عادةً أن يحاول التقاطه. وفي بايثون، ValueError وZeroDivisionError كلاهما استثناء رغم اسميهما.
ما الفرق بين throw و throws في جافا؟
throw عبارة ترفع استثناءً واحدًا في ذلك الموضع: throw new IllegalArgumentException("negative age");. أما throws فجزء من تصريح الدالة يسرد الاستثناءات المُتحقَّق منها التي قد تمررها الدالة إلى من استدعاها: void load() throws IOException. عندها يُلزم المترجم كل من يستدعيها بأن يلتقط ذلك الاستثناء أو يُعلنه هو أيضًا.
هل تجعل معالجة الاستثناءات الكود أبطأ؟
الدخول إلى كتلة try رخيص في معظم اللغات الحديثة. منذ بايثون 3.11 لا تكلّف try شيئًا تقريبًا ما لم يُرفع استثناء، والمترجمات الكبرى للغة C++ لا تضيف أي كلفة وقت تشغيل إلى المسار الذي لا يُرمى فيه شيء. لكن رفع استثناء والتقاطه أبطأ بكثير من اختبار if، ففي الحلقات الحساسة للأداء افحص الحالات الشائعة بدل الاعتماد على الاستثناءات.
Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن