View Categories

8.3. معالجة الاستثناءات

4 دقيقة

جدول المحتويات

8.3. معالجة الاستثناءات #

من الممكن كتابة برامج تُعالج استثناءات مُحدّدة. انظر إلى المثال التالي، الذي يطلب من المستخدم إدخال بيانات حتى يتم إدخال عدد صحيح صحيح، ولكنه يسمح للمستخدم بمقاطعة البرنامج (باستخدام Control-C أو أيًا كان ما يدعمه نظام التشغيل)؛ لاحظ أنه يتم الإشارة إلى المقاطعة التي يُنشئها المستخدم عن طريق إثارة استثناء KeyboardInterrupt.

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")
...

تعمل عبارة try كما يلي:

  • أولاً، تُنفَّذ عبارة try (العبارات بين الكلمتين المفتاحيتين try و except).
  • في حال عدم حدوث أي استثناء، يتم تخطي عبارة except وينتهي تنفيذ عبارة try.
  • في حال حدوث استثناء أثناء تنفيذ عبارة try، يتم تخطي بقية الجملة. ثم إذا تطابق نوعه مع الاستثناء المذكور بعد الكلمة المفتاحية except، يتم تنفيذ عبارة except، ثم يستمر التنفيذ بعد عبارة try.
  • في حال حدوث استثناء لا يتطابق مع الاستثناء المذكور في عبارة except، يتم تمريره إلى عبارات try الخارجية؛ أما في حال عدم العثور على معالج، فيُعتبر استثناءً غير معالج ويتوقف التنفيذ برسالة كما هو موضح أعلاه.

يمكن لعبارة try أن تحتوي على أكثر من عبارة استثناء واحدة، وذلك لتحديد معالجات لاستثناءات مختلفة. سيتم تنفيذ معالج واحد على الأكثر. تعالج المعالجات الاستثناءات التي تحدث في عبارة try المقابلة فقط، وليس في معالجات أخرى لعبارة try نفسها. يمكن لعبارة except تسمية استثناءات متعددة كمجموعة بين قوسين، على سبيل المثال:

... except (RuntimeError, TypeError, NameError):
...     pass

تكون الفئة في عبارة except متوافقة مع استثناء إذا كانت هي الفئة نفسها أو فئة أساسية منها (ولكن ليس العكس – فعبارة except التي تسرد فئة مشتقة لا تتوافق مع الفئة الأساسية). على سبيل المثال، سيطبع الكود التالي B، C، D بهذا الترتيب:

class B(Exception):
    pass
class C(B):
    pass
 class D(C):
    pass
for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

لاحظ أنه إذا تم عكس جمل الاستثناء (باستخدام except B أولاً)، فستكون قد طبعت  B, B, B — يتم تفعيل أول جملة استثناء مطابقة.

قد تحذف جملة الاستثناء الأخيرة اسم (أسماء) الاستثناء، لتكون بمثابة حرف بدل. استخدم هذا بحذر شديد، إذ يسهل إخفاء خطأ برمجي حقيقي بهذه الطريقة! يمكن استخدامه أيضًا لطباعة رسالة خطأ ثم إعادة إثارة الاستثناء (مما يسمح للمستدعي بمعالجة الاستثناء أيضًا):

import sys

 try:

    f = open('myfile.txt')

    s = f.readline()

    i = int(s.strip())

except OSError as err:

    print("OS error: {0}".format(err))

except ValueError:

    print("Could not convert data to an integer.")

except:

    print("Unexpected error:", sys.exc_info()[0])

    raise

تحتوي عبارة try … except على جملة else اختيارية، والتي، عند وجودها، يجب أن تتبع جميع جمل except. وهي مفيدة للكود الذي يجب تنفيذه إذا لم تُثر جملة try استثناءً. على سبيل المثال:

for arg in sys.argv[1:]:

    try:

        f = open(arg, 'r')

    except OSError:

        print('cannot open', arg)

    else:

        print(arg, 'has', len(f.readlines()), 'lines')

        f.close()

يُعد استخدام عبارة else أفضل من إضافة شيفرة برمجية إلى عبارة try لأنه يتجنب التقاط استثناء غير مقصود لم يُثره الشيفرة البرمجية المحمية بعبارة try … except.

عند حدوث استثناء، قد يكون له قيمة مرتبطة، تُعرف أيضًا باسم وسيطة الاستثناء. يعتمد وجود الوسيطة ونوعها على نوع الاستثناء.

قد تُحدد عبارة except متغيرًا بعد اسم الاستثناء. يرتبط المتغير بمثيل استثناء مع تخزين الوسيطات في instance.args. لتسهيل الأمر، تُعرّف حالة الاستثناء دالة __str__() بحيث يمكن طباعة الوسيطات مباشرةً دون الحاجة إلى الرجوع إلى .args. يمكنك أيضًا إنشاء استثناء أولًا قبل رفعه وإضافة أي سمات إليه حسب الرغبة.

>>> try:

...     raise Exception('spam', 'eggs')

... except Exception as inst:

...     print(type(inst))    # the exception instance

...     print(inst.args)     # arguments stored in .args

...     print(inst)          # __str__ allows args to be printed directly,

...                          # but may be overridden in exception subclasses

...     x, y = inst.args     # unpack args

...     print('x =', x)

...     print('y =', y)

...

<class 'Exception'>

('spam', 'eggs')

('spam', 'eggs')

x = spam

y = eggs

إذا كان للاستثناء وسائط، فسيتم طباعتها في الجزء الأخير (‘detail’) من الرسالة في حالة الاستثناءات غير المعالجة.

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

>>> def this_fails():

...     x = 1/0

...

>>> try:

...     this_fails()

... except ZeroDivisionError as err:

...     print('Handling run-time error:', err)

...

Handling run-time error: division by zero
error: Content is protected !!
Scroll to Top