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
= math.sqrt(4) square
وكذلك المعروف باسم “خطأ الحافَّة” (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 وغيرها. لكن ليس شرطًا أن يكون بها حتى تستفيد منه.
ومما يسهل الاحتراز من الأخطاء المنطقية: تجويد العبارة البرمجية.
ومن تجويد العبارة تسمية المتغيرات بما يدل على وظيفتها، مثل:
= 50
rate = 6
hours_per_day = 5
days = rate * hours_per_day * days
pay print(pay)
1500
وإن كان ليس من الخطأ النحوي كتابتها بطريقة مختلفة وبأسماء غير معبِّرة، إلا أنه فعلٌ غير مستحسن:
= 50, 6, 5
r, hpd, d = r * hpd * d
p 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
) هذه الحالة الخاطئة؛ فإن البرنامج يتم سيره:
وإليك تمثيل هذه الصورة بقطعة بايثون:
= True
some_condition
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
لنترك الخطأ ليصعد إلى الأعلى:
= True
some_condition
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()
أو بالتأكد من تحويل النوع مسبقًا.
مثال:
= 5
a = input('Enter a number: ')
b = a + int(b) result
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 else
IndexError
& 5. KeyError
السبب: الرقم الذي استعمل في عملية الإشارة list[index]
(قائمة) أو dict[key]
(قاموس) يشير لما هو خارج المجموعة. وهذا يؤدي إلى كوارث لو كان في لغة “غير آمنة” مثل سي (C) لأنها لا تتحقق من صحة المؤشر، إلا إذا فعلنا ذلك بأنفسنا. لكن في بايثون يتم كشف هذا الخطأ ورفعه حال وقوعه مباشرة، ونتعامل معه كاستثناء.
نحو:
= [10, 20, 30]
my_list = 3 idx
my_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):
= my_list[idx]
value else:
# do something else
أو بالاستجابة للاستثناء المرفوع:
try:
= my_list[idx]
value except IndexError:
# do something else
وكذلك في القاموس، نحو:
= {'A': 10, 'B': 20, 'C': 30}
my_dict = 'Z' key
= my_dict[key] value
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) Cell In[24], line 1 ----> 1 value = my_dict[key] KeyError: 'Z'
الحل: بالاحتراز بأن نشترط وجود المفتاح أصلاً في القاموس
if key in my_dict:
= my_dict[key]
value else:
# do something else
أو هكذا (تعيين قيمة افتراضية عند العدم):
= my_dict.get(key, 0) value
أو بالاستجابة للاستثناء المرفوع:
try:
= my_dict[key]
value except KeyError:
# do something else
AttributeError
& 7. NameError
السبب: استعمال متغير أو فعل قبل تعريفه.
AttributeError
(مثل: a.x
)NameError
(مثل: X
)= 10
a + X a
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[25], line 2 1 a = 10 ----> 2 a + X NameError: name 'X' is not defined
55) some_function(
--------------------------------------------------------------------------- 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 numpy
uv
فالأمر: uv add numpy
(وهو الذي ننصح به)تعريف الخطأ يكون بتعريف نوع جديد يرث من النوع Exception
، وهذا ما يحققه السطر الأول بين القوسين. وتستطيع أن ترث ممن يرث، فتتكون لديك فروع من هذا الخطأ:
class ParentError(Exception):
pass
class XError(ParentError):
pass
class YError(ParentError):
pass
flowchart 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:
'language'] = 'ar'
user[# ... rest of the code
save_to_database(user)
وبهذا يكون الإجراء قد تعامل مع الخطأ بنفسه.
وقد مثلنا له بفتح الملف، ونمثل له بمثال آخر: وهو صعود خطأ من إجراء تحويل القيمة النصية الآتية من المستخدم (guess
) إلى قيمة عدد صحيح (int
)؛ فإننا نستجيب لحدوث الخطأ ValueError
ونتعامل معه بإظهار رسالة تفيد المستخدم بمكمن الخطأ وكيفية إصلاحه من جهته: “القيمة ليست عددًا .. من فضلك أدخل قيمة عددية”.
def get_user_guess() -> int:
print('Please enter a number')
= input()
guess try:
= int(guess)
guess except ValueError:
print(f'The value "{guess}" is not a number')
= get_user_guess() # recursive call
guess 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):
/ b)
result.append(a return result
فالطريقة الصحيحة للتعامل مع مثل هذه الأخطاء، أن نتركها تصعد لوحدها، وندوِّن إمكان حصولها في شرح الإجراء (وهو النص الذي في أوَّل جسده).
من الخطأ في المنطق البرمجي إلتقاط جميع الأخطاء في الإجراءات باستعمال except Exception
وهو النوع الشامل لجميع الأخطاء. فهذه الطريقة لا تخبرنا بنوع الخطأ وبالتالي لا نتعامل معه بحسبه، وإنما غاية ما تحقق هو منع تصعيد الخطأ لأعلى طبقة، إذْ حين يحصل ذلك توقف بايثون البرنامج (وعندها تظهر سلسلة الاستدعاءات).
لكن الحالة النادرة تكون في أطر العمل (Frameworks) إذْ يجوز ذلك في قطع النص البرمجي التي يُراد لها الاستمرار، وإن فشلَ شيئٌ فيها. مثلاً: لا تريد للخادم أن يتوقف تمامًا بمجرد حصول خطأ واحد في أحد خيوط التنفيذ الخاصة بخدمة أحد الطلبات. ففي مثل ذلك يسوغ استخدام except Exception
الشاملة. انظر مثلاً قطعة النص البرمجي في إطار الويب فلاسك المسؤول عن استقبال الطلبات.
المراجع: