if x > 5
    print("x")Cell In[10], line 1 if x > 5 ^ SyntaxError: expected ':'
الخطأ في البرمجيات على نوعين:
فمن الخطأ الظاهر: الخطأ النحوي (Syntactic Error): وهو الخطأ في مبنى اللغة؛ أي: مخالفة قواعدها وقوانينها.
مثال ذلك فقد النطقتين الرأسيتين (:) كفاصلة للجملة الشرطية.. كما سيظهر الخطأ الآن في هذه القطعة:
if x > 5
    print("x")Cell In[10], line 1 if x > 5 ^ SyntaxError: expected ':'
كي تقرأ هذا الخطأ: انظر أولاً للسطر الأخير حيث كُتب SyntaxError فذاك نوع الخطأ. وكتب بعده تخصيص له، حيث قال: expected ':' .. أي: كان من المتوقع وجود : هنا. ثم انظر فوقه لتجد سهمًا صغيرًا يشير إلى المكان الذي يظن مفسر بايثون أن قد حصل فيه الخطأ الإملائي.
ومنه أيضًا عدم تطابق المسافاة البادئة للجمل ضمن القطعة الواحدة:
if True:
    print("x")
     print('y')Cell In[11], line 3 print('y') ^ IndentationError: unexpected indent
نوع هذا الخطأ هو IndentationError وهو نوع من الخطأ الإملائي.
وأما الخطأ الخفي فأساسه الخطأ المنطقي (Logical Error): وهو تعبيرٌ صحيحٌ نحويًّا لكنَّه لا يؤدي في الواقع إلى المقصود الذي أراده كاتبه منه. فالنية صحيحة لكن السهم أخطأ الهدف.
ومثال ذلك محاولة تفسير النص المكتوب بترميز مختلف عن الذي كُتِبَ به:
ومنه أيضًا: أن يريد المبرمج استعمال دالَّة التربيع (Square) فظنَّها math.sqrt لكن هذه (Square Root) أي: الجذر التربيعي. والصحيح أن يختار: math.pow(4, 2) لرفع 4 للقوة 2.
import math
square = math.sqrt(4)وكذلك المعروف باسم “خطأ الحافَّة” (Off-by-one error)، ويكاد أن يكون أشهر الأخطاء الخفية المنطقية في البرمجة.
نشرحه بمثال: النية هنا هي طباعة الأرقام بالعكس من الرقم الأعلى (5) إلى (0) بما في ذلك (0)، ولكن الحلقة تتوقف عند (1). وذلك أن آلية عمل النطاق (range) عدم شمول النهاية.
for i in range(5, 0, -1):
    print(i, end=' ')5 4 3 2 1 
والصحيح المطابق لنية الكاتب كان:
for i in range(5, -1, -1):
    print(i, end=' ')5 4 3 2 1 0 
وهي التي نقصدها حين نقول: بَق (Bug) بمعنى: مشكلة في البرنامج. ويسمى البرنامج الذي يساعد في إصلاح المشاكل البرمجية: المدقق (Debugger). وتسمى وعملية البحث عنها وإصلاحها: التدقيق (Debugging).
الأخطاء المنطقية صامتة. إذ لا يكتشفها المترجم، وتتسبب في تصرف البرنامج بشكل غير صحيح. الأخطاء المنطقية هي الأصعب في التتبع والإصلاح لأنها ليست واضحة. يمكن أن تكون ناجمة عن:
لكونها جملاً صحيحة في ذاتها؛ لا يستطيع البرنامج كشف هذه الأخطاء لوحده. بل يجب على من يُدركُ حقيقة المطلوب من البرنامج أن يتكفل بذلك. وهنا تكون الحاجة ماسَّة لجمل التوكيد: assert.
والتدقيق؛ إذْ أفضل طريقة لحل الأخطاء المنطقية هي تنفيذ القطعة البرمجية والنظر في الناتج، وتتبع المنطق مرة أخرى إلى النص البرمجي سطرًا بسطر. يمكنك استخدام عبارات الطباعة print لتصحيح الأخطاء وفهم تدفق البرنامج. وقد يكون الأفضل من ذلك استعمال المدقق (Debugger).
ومراجعة الأقران: بحيث يطلع على النص البرمجي شخص آخر، فإنه قد يرى منه ما تعذر عليك رؤيته. وقد يتم تنظيمه بين أعضاء الفريق الواحد بأحد برمجيات التعاون مثل: GitHub وGitLab وBitbucket وغيرها. لكن ليس شرطًا أن يكون بها حتى تستفيد منه.
ومما يسهل الاحتراز من الأخطاء المنطقية: تجويد العبارة البرمجية.
ومن تجويد العبارة تسمية المتغيرات بما يدل على وظيفتها، مثل:
rate = 50
hours_per_day = 6
days = 5
pay = rate * hours_per_day * days
print(pay)1500
وإن كان ليس من الخطأ النحوي كتابتها بطريقة مختلفة وبأسماء غير معبِّرة، إلا أنه فعلٌ غير مستحسن:
r, hpd, d = 50, 6, 5
p = r * hpd * d
print(p)1500
وفي هذا نصائح كثيرة، يراجع فيها دليل أسلوب الكتابة في بايثون.
ومن الخطأ الظاهر: الخطأ التشغيلي (Runtime Error)؛ أي الذي يصادَف أثناء عمل البرنامج. ويعبَّر عنه في عدة لغات باسم الاستثناء (Exception).
الاستثناء (Exception) هو إعلام بخروج البرنامج عن المسارات المعتادة إلى مسار لم تتم برمجته.
مثال ذلك:
فكل هذه تعتبر مسارات غير مثالية لكنها تحصل في ظروف واقعيَّة. فيجب كتابة قطع في البرنامج تتعامل معها. ولذا فإن بعض الممارسين لا يفضلون استعمال كلمة استثناء لأنَّ مثل ذلك يحصل كثيرًا فهو ليس خارجًا عن العادة؛ بل من الطبيعي أن يحصل ذلك في الواقع!
ولاحظ أننا في جميع الحالات السابقة نكشف الخطأ بفحص الحالة:
لكننا غالبًا ما كنا نتعامل مع إجراءات من مكتبات، وهي التي ترفع الاستثناءات.
وقد يتبادر لذهنك أن الاستثناء ما هو إلا حالة تكون في جملة الرجوع return. فهذا المثال (وهو مثال غير صحيح) يوضِّح هذه الفكرة:
def some_function():
    if some_condition:
        return Exception("something went wrong")
    return "everything is fine"وهذا صحيح في لغات برمجة أخرى غير بايثون؛ مثل: جو (Go) ورَسْت (Rust) وغيرها. لكن بايثون تشبه في هذا الأمر جافا (Java) وجافاسكريبت (JavaScript)، حيث تسمى رمي (throw) وأما في بايثون فتسمى رفع (raise) الاستثناءات.
وعملية الرفع (raise) مثل جملة الرجوع، إلا أنها تُجبِر الإجراء المستدعي على أحد خيارين:
أي أن هذه الآلية تجعل الاستثناء يرجع ويرجع إلى أن يصل لقطعة تتعامل معه، وإلا فإنه يخرج من البرنامج بالكلية فيتوقف. وهذه الحالة نسميها الانهيار (Crash)، وهي محمودة في الأغلب، إذْ قد يؤدي البرنامج إلى إيقاف الجهاز الذي يعمله عليه.
والشكل التالي يوضح أن الاستثناء يُرفع (raise) بعد التحقق (if) من حالة معيَّنة. فإن لم تحصل (False) هذه الحالة الخاطئة؛ فإن البرنامج يتم سيره:
وإليك تمثيل هذه الصورة بقطعة بايثون:
some_condition = True
def f4():
    print('f4')
def f3():
    print('f3:start')
    if some_condition:
        raise Exception("something went wrong")
    f4()            # <-- لن يتم تنفيذ هذا السطر بسبب رفع الاستثناء
    print('f3:end') # <-- لن يتم تنفيذ هذا السطر بسبب رفع الاستثناء
def f2():
    print('f2:start')
    try:
        f3()
    except Exception as e:
        print("Caught the exception:", e)
        print("dealing with it...")
        # ... some logic to deal with the error ...
    print('f2:end')
def f1():
    print('f1:start')
    f2()
    print('f1:end')
def main():
    print("main:start")
    f1()
    print("main:end")
main()main:start
f1:start
f2:start
f3:start
Caught the exception: something went wrong
dealing with it...
f2:end
f1:end
main:end
وغالبًا تظهر لك رسالة خطأ (كبيرة أحيانًا) وذلك يحصل حين يُترَك ولا يلتقط بجملة try-except إذْ أن بايثون في تلك الحالة تقوم بالآتي:
فهذا نفس المثال، لكننا سنحذف try-catch لنترك الخطأ ليصعد إلى الأعلى:
some_condition = True
def f4():
    print('f4')
def f3():
    print('f3:start')
    if some_condition:
        raise Exception("something went wrong")
    f4()            # <-- لن يتم تنفيذ هذا السطر بسبب رفع الاستثناء
    print('f3:end') # <-- لن يتم تنفيذ هذا السطر بسبب رفع الاستثناء
def f2():
    print('f2:start')
    f3()
    print('f2:end')
def f1():
    print('f1:start')
    f2()
    print('f1:end')
def main():
    print("main:start")
    f1()
    print("main:end")
main()main:start
f1:start
f2:start
f3:start
--------------------------------------------------------------------------- Exception Traceback (most recent call last) Cell In[18], line 28 25 f1() 26 print("main:end") ---> 28 main() Cell In[18], line 25, in main() 23 def main(): 24 print("main:start") ---> 25 f1() 26 print("main:end") Cell In[18], line 20, in f1() 18 def f1(): 19 print('f1:start') ---> 20 f2() 21 print('f1:end') Cell In[18], line 15, in f2() 13 def f2(): 14 print('f2:start') ---> 15 f3() 16 print('f2:end') Cell In[18], line 9, in f3() 7 print('f3:start') 8 if some_condition: ----> 9 raise Exception("something went wrong") 10 f4() # <-- لن يتم تنفيذ هذا السطر بسبب رفع الاستثناء 11 print('f3:end') Exception: something went wrong
فيما يلي نتأمل رسالة الخطأ التي ظهرت..
فترى في أول قطعة الإجراء الذي بدأ ذلك التسلسل كله وهو main:
Exception                                 Traceback (most recent call last)
Cell In[18], line 28
     25     f1()
     26     print("main:end")
---> 28 main()
ثم بعد ذلك ترى كومة الاستدعاءات (Stack Trace) وفي أسفل ذلك كله، ترى السبب المباشر للخطأ:
Cell In[18], line 25, in main()
     23 def main():
     24     print("main:start")
---> 25     f1()
     26     print("main:end")
Cell In[18], line 20, in f1()
     18 def f1():
     19     print('f1:start')
---> 20     f2()
     21     print('f1:end')
Cell In[18], line 15, in f2()
     13 def f2():
     14     print('f2:start')
---> 15     f3()
     16     print('f2:end')
Cell In[18], line 9, in f3()
      7 print('f3:start')
      8 if some_condition:
----> 9     raise Exception("something went wrong")
     10 f4()
     11 print('f3:end')
وأما السطر الأخير بعد ذلك كله، فإنه ملخص للخطأ، وهو أول ما يجب أن تقرأ:
Exception وهو أب جميع الأخطاء)something went wrong)Exception: something went wrong
try-except)تنفذ التعليمات في لغة البرمجة الأمرية (Imperative) كبايثون بحسب ترتيبها (من الأعلى إلى الأسفل). لكن عند حدوث خطأ، يتغيَّر سيْر الأوامر باستعمال جملة try-except. وشكل جملة التعامل مع الخطأ على هذا النحو:
try تتضمن الجملة التي نتوقع حدوث خطأٍ فيهاexcept Exception هي مثل if تنفذ ما تتضمنه إن كان الخطأ من نوع Excpetion (وهو أب جميع الأخطاء)
e فهو المتغير الذي يمثِّل تفاصيل الاستثناء إن وجدت؛ وعادة ما يكون رسالة نصيَّة تلخص الخطأelse تعمل عند عدم الخطأ (وفي هذا المثال لن تعمل أبدًا لأننا نتوقع حدوث أي خطأ على الإطلاق)finally وهي جُملة تعمل سواء وقع الخطأ أم لم يقع؛ لكنَّ بايثون تضمن عملها إن حصل خطأ أثناء التعامل مع الخطأdef do_something():
    print('before')
    try:
        # حاول تشغيل هذه القطعة
    except Exception as e:
        # إذا حدث خطأ من نوع Exception
        # فشغل هذه القطعة
    else:
        # وإن لم يحصل فهذه القعطة
    finally:
        # وشغل هذه على أية حال
        # سواءٌ حصل الخطأ أم لا
        # وفائدتها أنها تعمل قبل رجوع الخطأ لموضع النداء
    print('after')تم تعريف أنواع من الخطأ في بايثون متبوعة بكلمة Error (عُرفًا)، وذلك باعتبار حالات خطأ نمطية ومتكررة:
SyntaxErrorالسبب: خطأ نحوي في صياغة اللغة:
IndentationError)مثال:
if x > 5
    print("x")Cell In[19], line 1 if x > 5 ^ SyntaxError: expected ':'
الحل: اقرأ رسالة الخطأ وستدلُّك على السبب والموضع الذي حصل فيه الخطأ.
في الواقع هذا ليس من الأخطاء التشغيلية، بل هو خطأ نحوي / إملائي. ويمكن ضبط المحرر كي يكشفها لك قبل تشغيل البرنامج أصلاً.
TypeErrorالسبب:
len(1, 2))math.sqrt('nine') أو 5 + '5')الحل: الاحتراز بفحص النوع عن طريق الإجراء type() أو isinstance() أو بالتأكد من تحويل النوع مسبقًا.
مثال:
a = 5
b = input('Enter a number: ')
result = a + int(b)5 + '5'--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[20], line 1 ----> 1 5 + '5' TypeError: unsupported operand type(s) for +: 'int' and 'str'
ستجد الخلاصة في السطر الأخير:
TypeError+) غير متوافقة؛ وهي: العدد (int) والنص (str). ولاحظ أنه ذكر نوع العدد (int) لأنه قبله في ترتيب الكتابة.يمكن تفادي هذا النوع من الأخطاء باستعمال أدوات مثل mypy. لكن لا يتسع المقام لذكرها هنا.
ValueErrorالسبب: أن يكون النوع صحيحًا (فلا يحصُل TypeError) لكن القيمة غير مقبولة.
math.sqrt(-16) فالجذر التربيعي لا يقبل السالب.الحل: الاحتراز بفحص مدى القيمة قبل تنفيذ الأمر ، نحو:
if x >= 0:
    math.sqrt(x)
else:
    # do something elseIndexError & 5. KeyErrorالسبب: الرقم الذي استعمل في عملية الإشارة list[index] (قائمة) أو dict[key] (قاموس) يشير لما هو خارج المجموعة. وهذا يؤدي إلى كوارث لو كان في لغة “غير آمنة” مثل سي (C) لأنها لا تتحقق من صحة المؤشر، إلا إذا فعلنا ذلك بأنفسنا. لكن في بايثون يتم كشف هذا الخطأ ورفعه حال وقوعه مباشرة، ونتعامل معه كاستثناء.
نحو:
my_list = [10, 20, 30]
idx = 3my_list[idx]--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In[22], line 1 ----> 1 my_list[idx] IndexError: list index out of range
الحل: بأن نحترز باشتراط كون المؤشر لا يتعدى العنصر الأخير
if idx < len(my_list):
    value = my_list[idx]
else:
    # do something elseأو بالاستجابة للاستثناء المرفوع:
try:
    value = my_list[idx]
except IndexError:
    # do something elseوكذلك في القاموس، نحو:
my_dict = {'A': 10, 'B': 20, 'C': 30}
key = 'Z'value = my_dict[key]--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[24], line 1 ----> 1 value = my_dict[key] KeyError: 'Z'
الحل: بالاحتراز بأن نشترط وجود المفتاح أصلاً في القاموس
if key in my_dict:
    value = my_dict[key]
else:
    # do something elseأو هكذا (تعيين قيمة افتراضية عند العدم):
value = my_dict.get(key, 0)أو بالاستجابة للاستثناء المرفوع:
try:
    value = my_dict[key]
except KeyError:
    # do something elseAttributeError & 7. NameErrorالسبب: استعمال متغير أو فعل قبل تعريفه.
AttributeError (مثل: a.x)NameError (مثل: X)a = 10
a + X--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[25], line 2 1 a = 10 ----> 2 a + X NameError: name 'X' is not defined
some_function(55)--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[26], line 1 ----> 1 some_function(55) NameError: name 'some_function' is not defined
class A:
    pass
a = A()
a.x--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[27], line 5 2 pass 4 a = A() ----> 5 a.x AttributeError: 'A' object has no attribute 'x'
a.do_something()--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[28], line 1 ----> 1 a.do_something() AttributeError: 'A' object has no attribute 'do_something'
ModuleNotFoundErrorالسبب: فشل جُملة الاستيراد import numpy
الحل:
pip فالأمر: pip install numpyuv فالأمر: uv add numpy (وهو الذي ننصح به)تعريف الخطأ يكون بتعريف نوع جديد يرث من النوع Exception، وهذا ما يحققه السطر الأول بين القوسين. وتستطيع أن ترث ممن يرث، فتتكون لديك فروع من هذا الخطأ:
class ParentError(Exception):
    pass
class XError(ParentError):
    pass
class YError(ParentError):
    passflowchart TD
    A[ParentError] --> B[XError]
    A --> C[YError]
وهذا الإجراء يحصل فيه الخطأ بطريقة مصطنعة لكنها توضح ما نريد، وهو الخطأ الفرعي XError الذي يرث من الخطأ الأصلي ParentError:
def do_something():
    raise XError('Something went wrong')ثم حين نفحص، نستطيع أن نطابق بالأصل أو الفرع:
try:
    do_something()
except ParentError as e:
    print("caught you:", e)caught you: Something went wrong
يهندس المبرمج الإجراءات بحيث تكون نتيجة كل واحدٍ منها مبنيًّا على معطياته المباشرة؛ بحيث لو تم تمرير نفس المعطيات فإن نفس النتائج تخرج. فأن تقليل العوامل المؤثرة في كل خطوة يجعل التحقق من صحتها أسهل. ثم يتم تركيب البرنامج بمجموعها لتعمل جميع العوامل؛ وحينها نكون أقدر على التركيب الصحيح.
وذكرنا أن الإجراء له أحد أمرين لا ثالث لهما:
والرفع يتسلسل حتى يخرج من البرنامج؛ حيث يصل إلى الإنسان (سواءً المستفيد أو المبرمج): برسالة مفادها أمران:
فأما حين يستوعبه السياق؛ فلدينا طريقتان لمعالجة الخطأ:
هذا الإجراء مسؤول عن تهيئة البرنامج، ومن خطواته أنه يقرأ ملف الضبط المخصص custom_config.json. وفي هذه الحالة قد يحصل خطأ. إذْ قد لا يكون الملف موجودًا أصلا!
def initialize_program():
    # ... code before
    file = open('custom_config.json')
    # ... code afterففي هذه الحالة يحصل استثناء من نوع FileNotFoundError.
فإذا اخترنا طريق الاحتراز فإننا أولاً نلتمس وجوده قبل فتحه. ونتعامل مع حالة الخطأ هذه (أي: عدمه)، بأن -مثلاً- نقرأ ملف الضبط الافتراضي default.json بدلاً من الملف المطلوب. والفرق بين هذا الملف والذي قبله أننا موقنون بوجوده، والأوَّل وجوده مظنون إذْ الذي يضعه هو المستخدم.
def initialize_program():
    # ... code before
    if os.path.exists('custom_config.json'):
        file = open('custom_config.json')
    else:
        file = open('default.json')
    # ... code afterوقد تقول، حسنًا ماذا يكون لو عُدم ملف default.json أيضًا؟ فنقول: لا بأس أن يحصل خطأ يوقف البرنامج. إذْ ذاك يعتبر خطأ منطقيًّا يجب إصلاحه بإضافة الملف، وليس ثمة مجال لأن يصلحه النص البرمجي بنفسه .. وفي هذه الحالة؛ الأفضل أن نترك الخطأ ليتفاقم وينهار البرنامج فوْر وقوعه لنكتشفه ونصلحه.
أما الاستجابة فتكون بجملة try-except على النحو التالي:
def initialize_program():
    # ... code before
    try:
        file = open('custom_config.json')
    except FileNotFoundError:
        file = open('default.json')
    # ... code afterما هو الأفضل إذاً؟
أما استعمال الاحتراز ففيه شك بوجود فجوة زمنية بين عمليتي الإدراك والفعل. فالحاسب تتوارد عليه البرمجيات، ولا يستأثر به برنامج واحد. فإذا تغيَّر الحال في هذه الفجوة الزمنية (بعد إدراك وجود الملف .. تم حذفه) فعاد بالنقض على صواب الإدراك؛ لذلك تكون الاستجابة أضمن من الاحتراز فيما هو عُرضة لحالة التسابق.
مثاله الاحتراز مما لو لم تكن إحدى الخصائص موجودة (خطأ جديد)، فيمكن تعيينها بقيمة ابتدائية (الحل)، ولا يلزمنا تصعيد الخطأ. فهنا ننظر في المفتاح language هل هو موجود في القاموس user .. فإن لم توجَد عيَّنا له قيمة افتراضية: ar وأكمل الإجراء سيره:
def save_user(user: dict):
    if 'language' not in user:
        user['language'] = 'ar'
    # ... rest of the code
    save_to_database(user)وبهذا يكون الإجراء قد تعامل مع الخطأ بنفسه.
وقد مثلنا له بفتح الملف، ونمثل له بمثال آخر: وهو صعود خطأ من إجراء تحويل القيمة النصية الآتية من المستخدم (guess) إلى قيمة عدد صحيح (int)؛ فإننا نستجيب لحدوث الخطأ ValueError ونتعامل معه بإظهار رسالة تفيد المستخدم بمكمن الخطأ وكيفية إصلاحه من جهته: “القيمة ليست عددًا .. من فضلك أدخل قيمة عددية”.
def get_user_guess() -> int:
    print('Please enter a number')
    guess = input()
    try:
        guess = int(guess)
    except ValueError:
        print(f'The value "{guess}" is not a number')
        guess = get_user_guess() # recursive call
    return guessوبهذا يكون الإجراء يصحح نفسه حتى يضمن عند الرجوع return guess أنه أتمَّ وظيفته بشكل صحيح.
في هذا المثال نريد أن نُلزم كون وحدة القياس إحدى القيمتين: C (سيليلوز) أو F (فهرنهايت). وإلا فلا حيلة للإجراء أن يتمَّ وظيفته. لذا نرفع خطأ جديدًا بجملة الرفع raise على هذا النحو:
def convert_temperature(value: float, unit: str) -> float:
    if unit not in {'C', 'F'}:
        raise ValueError(f"Invalid unit: {unit}")
    
    if unit == 'C':
        return value * 9/5 + 32
    elif unit == 'F':
        return (value - 32) * 5/9لاحظ أننا اخترنا ValueError لما تقدَّم بيانه عن أن هذا النوع من الأخطاء هو لما يكون صحيح النوع (type) لكن خاطئ القيمة.
فإن من الأخطاء ما يتعذر على البرنامج معالجته بنفسه:
ففي هذا المثال عملية قسمة؛ ونتوقع بحسب معرفتنا الرياضية بإمكان حصول القسمة على صفر في (a / b)، فيرتفع بذلك خطأ اسمه ZeroDivisionError من تلك العملية. والحقيقة أننا في هذا السياق ليس لنا أن نعدِّل الرقم، بل نريد لمن استدعى الإجراء أن يعلم بالخطأ، ولسنا نريد إضافة معلومة أخرى فوق ذلك. لذا نسكت عنه (أي: لا نضع try-except) وهذا يجعله ينتقل مباشرة للإجراء المستدعي.
def divide_lists(list1: list, list2: list) -> list:
    """Divide the elements of list1 by the elements of list2
    :raises ZeroDivisionError: If the denominator `b` is zero.
    """
    result = []
    for a, b in zip(list1, list2):
        result.append(a / b)
    return resultفالطريقة الصحيحة للتعامل مع مثل هذه الأخطاء، أن نتركها تصعد لوحدها، وندوِّن إمكان حصولها في شرح الإجراء (وهو النص الذي في أوَّل جسده).
من الخطأ في المنطق البرمجي إلتقاط جميع الأخطاء في الإجراءات باستعمال except Exception وهو النوع الشامل لجميع الأخطاء. فهذه الطريقة لا تخبرنا بنوع الخطأ وبالتالي لا نتعامل معه بحسبه، وإنما غاية ما تحقق هو منع تصعيد الخطأ لأعلى طبقة، إذْ حين يحصل ذلك توقف بايثون البرنامج (وعندها تظهر سلسلة الاستدعاءات).
لكن الحالة النادرة تكون في أطر العمل (Frameworks) إذْ يجوز ذلك في قطع النص البرمجي التي يُراد لها الاستمرار، وإن فشلَ شيئٌ فيها. مثلاً: لا تريد للخادم أن يتوقف تمامًا بمجرد حصول خطأ واحد في أحد خيوط التنفيذ الخاصة بخدمة أحد الطلبات. ففي مثل ذلك يسوغ استخدام except Exception الشاملة. انظر مثلاً قطعة النص البرمجي في إطار الويب فلاسك المسؤول عن استقبال الطلبات.
المراجع: