يشرح هذا البرنامج التعليمي معالجة الاستثناءات في Python باستخدام كتلة Try except بمساعدة أمثلة البرمجة:
قد يتسبب نوعان من الأخطاء في توقف برنامج Python فجأة ، أي بناء الجملة أخطاء و استثناءات . في هذا البرنامج التعليمي ، سنناقش نوع الخطأ الثاني (الاستثناءات) ضمن عدة مواضيع مهمة.
سنستفيد كثيرًا من معالجة الاستثناءات في تطبيقنا مثل:
- إنشاء تطبيق قوي.
- إنشاء رمز نظيف وخالي من الأخطاء.
تجربة Python باستثناء
أحد الأخبار الجيدة هو أن Python بها عدد جيد من الاستثناءات المضمنة للقبض على الأخطاء في التعليمات البرمجية الخاصة بنا. أيضًا ، يمنحنا الفرصة لإنشاء استثناءات مخصصة عندما لا يناسب أي من الاستثناءات المضمنة احتياجاتنا.
ما هو الاستثناء
إذن ما هو الاستثناء في Python؟ حسنًا ، بعبارات بسيطة ، كلما حاول مترجم Python تنفيذ تعليمات برمجية غير صالحة ، فإنه يثير استثناءً ، وفي الحالات التي لا يتم فيها معالجة مثل هذا الاستثناء ، فإنه يعطل التدفق الطبيعي لتعليمات البرنامج ويطبع traceback.
لنقم بإنشاء رمز غير صالح ونرى كيف سيستجيب مترجم Python.
افتح قشرة Python وقم بتشغيل الكود التالي.
>>> 50/0
هذا واحد من الأخطاء الأكثر شيوعًا في البرمجة. يحاول الكود أعلاه قسمة الرقم 50 على 0 (صفر). بايثونمتغير openFile قبل تعيينه.
خدعة صغيرة هنا هي استخدام معالجات الاستثناءات داخل الكتلة النهائية.
def readFile(file_path): try: openFile = open(file_path,'r') # Open a file as read-only print(openFile.readline()) # Read first line of file content except FileNotFoundError as ex: print(ex) finally: try: print("Cleaning...") openFile.close() except: # catches all exceptions pass # Ignore this error because we don't care. if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
إذا كانت كتلة try-block الخاصة بنا ترفع FileNotFoundError ، فسنحصل على الناتج التالي
Raise Exception
أحد الأخبار الجيدة حول استثناءات Python هو أننا نستطيع عن قصد تربيتهم. يتم رفع الاستثناءات باستخدام العبارة lift .
تحتوي العبارة lift على النحو التالي:
raise [ExceptionName[(*args: Object)]]
افتح Terminal وقم برفع أي كائن استثناء من استثناءات Python المضمنة. على سبيل المثال ، إذا رفعنا ZeroDivisionError:
>>> raise ZeroDivisionError("Can't divide by zero")
سنحصل على traceback:
لذا ، لماذا من المهم رفع الاستثناءات؟
- عند العمل مع استثناءات مخصصة.
- أثناء فحوصات السلامة.
فئات استثناء مخصصة
الاستثناء المخصص هو الاستثناء الذي تقوم بإنشائه للتعامل مع الأخطاء الخاصة بحاجتك. الحيلة هي أننا نحدد فئة مشتقة من الكائن استثناء ، ثم نستخدم تعليمة الارتفاع لرفع فئة الاستثناء الخاصة بنا.
لنفترض أننا نريد التحقق من إدخال المستخدم والتأكد قيمة الإدخال ليست سالبة (التحقق من صحة العقل). بالطبع ، يمكننا رفع قيمة استثناء Python ValueError ولكننا نرغب في تخصيص الخطأ من خلال إعطائه اسمًا محددًا وواضحًا بذاته مثل InputIsNegativeError . لكن هذا الاستثناء ليس مدمجًا في Pythonاستثناء.
لذلك أولاً ، نقوم بإنشاء الفئة الأساسية الخاصة بنا والتي ستشتق من الاستثناء.
class CustomError(Exception): "Base class exception for all exceptions of this module" pass
ثم نقوم بإنشاء فئة الاستثناء الخاصة بنا والتي سترث الفئة الأساسية وسوف تتعامل مع الخطأ المحدد الخاص بنا.
class InputIsNegativeError(CustomError): """Raised when User enters a negative value""" pass
لنختبر هذا
try: value = int(input()) if value 0: raise InputIsNegativeError # Raise exception if value is negative except InputIsNegativeError: # catch and handle exception print("Input value shouldn't be negative")
طلب الكود أعلاه لإدخال المستخدم ، وتحقق مما إذا كان سالبًا. إذا كان هذا صحيحًا ، فإنه يثير استثناءنا المخصص InputIsNegativeError والذي تم اكتشافه لاحقًا في عبارة الاستثناء.
أدناه هو الرمز الكامل:
class CustomError(Exception): "Base class exception for all exceptions of this module" pass class InputIsNegativeError(CustomError): """Raised when User enters a negative value""" pass if __name__ == '__main__': try: value = int(input("Input a number: ")) if value 0: raise InputIsNegativeError # Raise exception if value is negative except InputIsNegativeError: # catch and handle exception print("Input value shouldn't be negative")
إذا كانت قيمة الإدخال هو رقم سالب مثل -1 ، ثم سيكون لدينا الإخراج:
تحقق من مستند Python لمزيد من التفاصيل حول استثناءات Python المخصصة. 13> أسئلة متكررة
س # 1) كيف تتعامل بايثون مع استثناء؟
الإجابة: Python تتعامل مع الاستثناءات باستخدام عبارة try-except . يتم وضع الكود الذي يمكنه رفع استثناء وتنفيذه في كتلة المحاولة بينما يحتفظ باستثناء الكتلة بالكود الذي سيتعامل مع الاستثناءات في حالة ظهور أي منها.
Q # 2) ما الذي يثير استثناء في Python؟
الإجابة: عندما يواجه مترجم Python رمزًا غير صالح ، فإنه يثير استثناءً ، وهو طريقة Python الخاصة لإخبارنا بحدوث شيء غير متوقع. يمكننا أيضًا رفع الاستثناءات عمدًا باستخدام lift .
Q # 3) كيف تتعامل Python مع الاستثناءات المتعددة؟
الإجابة: بايثون تتعامل مع استثناءات متعددةباستخدام إما كتلة واحدة باستثناء أو عدة كتل باستثناء.
بالنسبة لكتلة واحدة ، يتم تمرير الاستثناءات على هيئة مجموعة: باستثناء (استثناء 1 ، استثناء 2 ، .. ، ExceptionN) واختبارات Python لمباراة من اليمين إلى اليسار. في هذه الحالة ، يتم اتخاذ نفس الإجراء لكل استثناء.
هناك طريقة أخرى للقبض على جميع الاستثناءات وهي ترك اسم الاستثناء بعد الكلمة الأساسية باستثناء.
except: # handle all exceptions here
الطريقة الثانية هي لاستخدام كتلة استثناء لكل استثناء:
except Exception1: # code to handle Exception1 goes here except Exception2: # code to handle Exception2 goes here except ExceptionN: # code to handle ExceptionN goes here
بهذه الطريقة ، يمكنك اتخاذ إجراءات منفصلة لكل استثناء.
س # 4) لماذا تعتبر معالجة الاستثناءات مهمة في بايثون؟
الإجابة: تتمثل فائدة معالجة الاستثناءات في Python في أنه يمكننا إنشاء تطبيقات قوية ونظيفة وخالية من الأخطاء. لن نرغب في تعطل كود الإنتاج بسبب بعض الأخطاء ، لذا فإننا نتعامل مع الأخطاء ونحافظ على تشغيل تطبيقنا.
Q # 5) كيف تتجاهل استثناء في Python؟
الإجابة: لتجاهل استثناء في Python ، استخدم الكلمة الأساسية pass في كتلة الاستثناء. لنفترض أننا نريد تجاهل استثناء ValueError. سنفعل ذلك بهذه الطريقة:
except ValueError: pass
ما لم تكن تعرف ما تفعله ، فمن السيئ تجاهل الاستثناءات. على الأقل ، أبلغ المستخدم بجميع الأخطاء المحتملة.
الاستنتاج
في هذا البرنامج التعليمي ، قمنا بتغطية: استثناءات Python، Traceback؛ كيفية التعامل مع الاستثناءات مع حاول / باستثناء / عدا ذلك / أخيرًا ، كيفية رفع الاستثناءات ، وأخيرًا كيفية إنشاء استثناءات مخصصة خاصة بنا.
شكرًا على القراءة!
يرى المترجم أن هذا عملية غير صالحة ويثير ZeroDivisionError ، ويعطل البرنامج ، ويطبع traceback.
يمكننا أن نرى بوضوح أن ZeroDivisionError هو الاستثناء الذي تم إطلاقه. إنها بالفعل طريقة بايثون الخاصة لإخبارنا أنه ليس من الجيد قسمة رقم على صفر. رغم أنه في لغات أخرى مثل JavaScript ، فإن هذا ليس خطأ ؛ و python تمنع تمامًا هذه الممارسة.
أيضًا ، من المهم أن تعرف أن هذا مجرد كائن استثناء وأن Python بها العديد من هذه الكائنات المضمنة. تحقق من وثائق Python الرسمية هذه للاطلاع على جميع استثناءات Python المضمنة.
فهم Traceback
قبل أن نتعامل مع الاستثناءات ، أعتقد أنه سيساعد على فهم ما سيحدث بالضبط إذا كانت الاستثناءات لم يتم التعامل معها وكيف أن Python تبذل قصارى جهدها لإبلاغنا بخطأنا.
عندما تواجه Python خطأً ، فإنها تثير استثناءً. إذا لم يتم التعامل مع هذا الاستثناء ، فإنه ينتج بعض المعلومات تسمى Traceback. إذن ، ما هي المعلومات التي يحتوي عليها هذا التتبع؟ أثار.
على الرغم من أنهامربكًا بعض الشيء ، نحن نعد بأن المثال التالي سيجلب مزيدًا من الضوء لفهمنا.
تذكر تتبع traceback الذي تم طباعته من قسمة 50 على 0 أعلاه ، يمكننا أن نرى أن traceback يحتوي على المعلومات التالية:
- ملف "": هذا يخبرنا أن هذا الرمز تم تشغيله من محطة طرفية لوحدة التحكم.
- سطر 1: هذا يخبرنا أن الخطأ حدث في رقم السطر هذا.
- ZeroDivisionError: القسمة على صفر: يخبرنا ما هو الاستثناء الذي أثير وما سبب ذلك.
لنجرب مثالًا آخر و ربما ترى كيف تبدو مكدس الاستدعاءات . افتح محررًا ، وأدخل الرمز أدناه واحفظه باسم tracebackExp .py
def stack1(numb): # 1 div = 0 # 2 stack2(numb, div) # 3 def stack2(numb, div): # 5 compute = numb/div # 6 print(compute) # 7 if __name__ == '__main__': # 9 numb = 5 # 10 stack1(numb) # 11
افتح محطة طرفية في الدليل حيث تم العثور على هذا الملف وتشغيله.
python tracebackExp.py
سترى التتبع التالي:
قد يبدو التتبع أعلاه محيرًا ولكنه في الحقيقة ليس كذلك. توصلت Pythonistas إلى أفضل طريقة لقراءة traceback ، والتي تكون من أسفل إلى أعلى . لذلك ، دعونا نستخدم هذه الحكمة لمحاولة فهم ما يجب أن تقدمه عملية التتبع هذه.
- في الجزء السفلي ، نحصل على الاستثناء الذي تم طرحه ولماذا تم رفعه.
- بالانتقال إلى الأعلى ، نحصل على اسم الملف tracebackExp .py حيث حدث هذا الخطأ ، والحساب الذي تسبب في هذا الخطأ حساب = numb / div ، ومكدس الوظيفة 2 ، وسطر رقم الارتباط 6 حيث تم إجراء هذا الحساب .
- بالانتقال لأعلى ، نرى أن الدالة stack2 لديناتم استدعاؤه في مكدس الوظيفة 1 في السطر رقم 3.
- بالانتقال إلى الأعلى ، نرى أنه تم استدعاء دالة المكدس 1 في السطر رقم 11. العلامة & lt؛ الوحدة & GT؛ يخبرنا أن هذا هو الملف الذي يتم تنفيذه.
استثناءات Python الشائعة
تحدد مكتبة Python عددًا هائلاً من الاستثناءات المضمنة. يمكنك التحقق من توثيق Python أو استدعاء الوظيفة المضمنة local () على النحو التالي:
>>> dir(locals()['__builtins__'])
لن نحاول معالجة كل هذه الاستثناءات ، لكننا سنرى بعض الاستثناءات الشائعة التي من المحتمل أن تصادفها.
# 1) TypeError
يتم رفعها عند تطبيق عملية أو وظيفة على كائن من نوع غير مناسب.
مثال 1
جرب البرنامج أدناه. يأخذ مقسومًا ومقسومًا عليه ، ثم يحسب ويطبع نتيجة قسمة المقسوم على المقسوم عليه.
def compute_division(): dividend = int(input("Enter the dividend: ")) # cast string to int divisor = input("Enter the divisor: ") # no casting # Compute division result = dividend/divisor # print result print("The result of {}/{} is: {}".format(dividend, divisor, result)) if __name__ == '__main__': result = compute_division()
نطلب قيمة المقسوم والمقسوم عليه من المستخدم ، لكننا ننسى إلقاء سلسلة المقسوم القيمة في عدد صحيح. لذلك ، ننتهي بنوع المقسوم الذي يكون عددًا صحيحًا ( int ) ونوع المقسوم عليه هو سلسلة ( str ). نحصل بعد ذلك على TypeError لأن عامل القسمة (/) لا يعمل على السلاسل.
قد يثير اهتمامك معرفة ذلك بخلاف Python ، تحتوي Javascript على نوع الإكراه الذي يحول أساسًا أحد أنواع المعامل إلى قيمة مكافئة لنوع المعامل الآخر عندما تكون المعاملات منأنواع مختلفة.
# 2) ValueError
يظهر هذا عندما تتلقى عملية أو دالة وسيطة من النوع الصحيح ولكن قيمة غير مناسبة.
مثال 2
ضع في اعتبارك برنامجنا في المثال 1 أعلاه.
إذا أدخل المستخدم قيمة أبجدية رقمية لأرباح الأسهم مثل '3a' ، فسيرفع برنامجنا استثناء ValueError. هذا لأنه ، على الرغم من أن طريقة Python int () تأخذ أي رقم أو سلسلة وتعيد كائنًا صحيحًا ، يجب ألا تحتوي قيمة السلسلة على أحرف أو أي قيمة غير رقمية.
# 3) AttributeError
يظهر هذا الاستثناء أثناء تعيين أو الإشارة إلى سمة غير موجودة.
مثال 3
فكر في البرنامج أقل. يأخذ عددًا ويحسب جذره التربيعي باستخدام وحدة الرياضيات في بايثون
import math # import math library to gain access to its code def compute_square_root(number): # compute the square root using the math library result = math.sqr(number) return result if __name__ == '__main__': # get input to compute from user number = int(input("Compute Square root of: ")) # call function to compute square root
عندما يقوم المستخدم بإدخال رقم ، يحاول برنامجنا استخدام دالة من وحدة الرياضيات لحساب جذرها التربيعي ولكن هذا فقط هنا ، لقد ارتكبنا خطأ. بدلاً من sqrt ، كتبنا عن طريق الخطأ sqr غير موجود في وحدة الرياضيات. إلى الاستثناء AttributeError الذي يتم رفعه. يرتكب معظمنا هذا النوع من الأخطاء كثيرًا. لذا ، فأنت لست وحدك.
التعامل مع الاستثناءات باستخدام المحاولة باستثناء
بصفتك مبرمجًا ، فإن الشيء الوحيد الذي سيقضي معظمنا وقتنا فيه هو كتابة رمز قويمرن. رمز لا ينكسر بسبب بعض الأخطاء. في Python ، يمكننا تحقيق ذلك من خلال إرفاق عباراتنا داخل try - باستثناء عبارة .
Python try-except statement
عبارة try-except تحتوي على البنية التالية:
try: #your code goes here except """Specify exception type(s) here""": #handle exception here
دعونا نضع الكود في tracebackExp .py داخل عبارة try-except.
def stack1(numb): # 1 div = 0 # 2 stack2(numb, div) # 3 def stack2(numb, div): # 5 try: # 6 compute = numb/div # 7 print(compute) # 8 except ZeroDivisionError as zde: # 9 print(zde) # 10 if __name__ == '__main__': # 12 numb = 5 # 13 stack1(numb) # 14 print("program continuous") # 15
سيؤدي تشغيل هذا الرمز إلى إنتاج الإخراج
هذه هي الطريقة التي تعمل بها عبارة try-except. تنفذ بايثون الكود في كتلة المحاولة السطر 7-8 . إذا لم يتم العثور على رمز غير صالح ، فسيتم تخطي الكود الموجود في كتلة الاستثناء السطر 10 ويستمر التنفيذ.
ولكن ، إذا تم العثور على رمز غير صالح ، فسيتوقف التنفيذ فورًا في حاول block والتحقق مما إذا كان الاستثناء الذي تم رفعه يتطابق مع الاستثناء الذي قدمناه في بيان الاستثناء السطر 9 . إذا تطابقت ، فسيتم تنفيذ كتلة الاستثناء وتستمر. إذا لم يحدث ذلك ، فسيقوم البرنامج بالمقاطعة.
تحتوي كتلة try-block عادةً على الكود الذي قد يثير استثناءً بينما يقوم باستثناء block بإمساك الاستثناء ومعالجته.
Handling Multiple استثناءات باستثناء
يمكننا التعامل مع استثناءات متعددة إما "باستثناء" واحدة أو "استثناءات" متعددة. كل هذا يتوقف على الطريقة التي تريد التعامل بها مع كل استثناء.
# 1) التعامل مع استثناءات متعددة مع واحدة باستثناء
try: #your code goes here except(Exception1[, Exception2[,...ExceptionN]]]): #handle exception here
يتم استخدام هذه الطريقة عندما نشك في أن الكود الخاص بنا قدتثير استثناءات مختلفة ونريد اتخاذ نفس الإجراء في كل حالة. لذلك ، إذا وجد مترجم Python تطابقًا ، فسيتم تنفيذ الكود المكتوب في كتلة الاستثناء.
لنفكر في مثال كود Python أدناه
def get_fraction(value, idx): arr = [4,5,2,0] # a list of numbers idx_value = arr[idx] # if idx is > arr length, IndexError will be raised value/idx_value # if idx_value == 0, ZeroDivisionError will be raised if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 3 # call function in a try-except statement. try: result = get_fraction(value, idx) print("Fraction is ", result) except (IndexError, ZeroDivisionError) as ex: print(ex)
لدينا اثنان الاستثناءات المحتملة التي يمكن رفعها هنا ، ZeroDivisionError و IndexError . إذا تم رفع أي من هذه الاستثناءات ، فسيتم تنفيذ كتلة الاستثناء.
في الكود أعلاه ، idx = 3 ، لذلك يصبح idx_ value 0 و value / idx_ value سترفع ZeroDivisionError
# 2) التعامل مع استثناءات متعددة مع استثناءات متعددة
try: #your code goes here except Exception1: #handle exception1 here except Exception2: #handle exception2 here except ExceptionN: #handle exceptionN here
إذا كنا نفضل التعامل كل استثناء على حدة ، فهذه هي الطريقة التي يمكنك القيام بها.
ضع في اعتبارك مثال كود Python أدناه
def get_fraction(value, idx): arr = [4,5,2,0] # a list of numbers idx_value = arr[idx] # if idx is > arr length, IndexError will be raised value/idx_value # if idx_value == 0, ZeroDivisionError will be raised if __name__ =='__main__': # set 'value' and 'idx' value = 54 idx = 5 # call function in a try-excepts statement. try: result = get_fraction(value, idx) print("Fraction is ", result) except IndexError: print("idx of {} is out of range".format(idx)) except ZeroDivisionError: print("arr[{}] is 0. Hence, can't divide by zero".format(idx)) except Exception as ex: print(ex) print("Not sure what happened so not safe to continue, \ app will be interrupted") raise ex
نلاحظ هنا أنه تم استخدام Exception في آخر بيان باستثناء . وذلك لأن استثناء الكائن الاستثناء يطابق أي استثناء. لهذا السبب ، يجب أن يكون دائمًا أخيرًا ، نظرًا لأن Python ستتوقف عن التحقق من معالجات الاستثناءات الأخرى بمجرد تطابق واحد.
في الكود أعلاه ، idx = 5 ، وبالتالي arr [idx ] سيرفع خطأ الفهرس لأن idx أكبر من طول القائمة arr
أيضًا ، لست متأكدًا من الاستثناء الذي أثاره تطبيقك ، فهو ليس آمنًا على الإطلاق لمواصلة التنفيذ. لهذا السبب لدينا النوع Exception للقبض على أي استثناءات غير متوقعة. ثم نبلغالمستخدم ومقاطعة التطبيق عن طريق رفع نفس الاستثناء.
جرب جملة أخرى
هذه ميزة اختيارية لمعالجة الاستثناء وتسمح لك بإضافة الكود الذي ترغب فيه تشغيل عند عدم حدوث أخطاء. إذا حدث خطأ ، فلن يتم تشغيل مجموعة else.
ضع في اعتبارك مثال كود Python أدناه ، افتح المحرر واحفظ الكود باسم elseTry.py
def fraction_of_one(divisor): value = 1/divisor # if divisor is zero, ZeroDivisionError will be raised return value if __name__ == '__main__': while True: try: # Get input from the user. # if input is not a valid argument for int(), ValueError will be raised divisor = int(input("Enter a divisor: ")) # call our function to compute the fraction value = fraction_of_one(divisor) except (ValueError, ZeroDivisionError): print("Input can't be zero and should be a valid literal for int(). Please, try again!") else: print("Value: ", value) break
نحصل على مدخلات من المستخدم ونستخدمها لتقسيم 1. لدينا استثناءان محتملان هنا ، إدخال مستخدم غير صالح سيؤدي إلى ValueError و صفر (0) والذي سيؤدي إلى ZeroDivisionError . تعالج عبارة الاستثناء الخاصة بنا هذه الأخطاء.
الآن ، نريد طباعة قيمة قيمة . تتأكد مجموعة else-block الخاصة بنا من طباعتها فقط إذا تم تنفيذ كتلة try دون أخطاء. هذا مهم لأنه في حالة حدوث خطأ في كتلة المحاولة الخاصة بنا ، فإن القيمة ستكون غير محددة. لذلك ، سيؤدي الوصول إليه إلى ظهور خطأ آخر.
قم بتشغيل الكود أعلاه باستخدام Python elseTry.py
الإخراج أعلاه يوضح ذلك للإدخال الأول ، قمنا بكتابة 0 والضغط على ENTER. منذ أن تلقى المقسوم عليه 0 ، قام 1 / المقسوم عليه برفع خطأ في القسمة . كان الإدخال الثاني الخاص بنا هو k وهو غير صالح لـ int () ، ومن هنا تم رفع الاستثناء خطأ القيمة .
لكن الإدخال الأخير كان 9 وهو صالح وكأحد نتيجة ، حصلنا على قيمة " value " مطبوعة كـ 0.1111111111111111
حاول أخيرًاالعبارة
هذه أيضًا ميزة اختيارية لمعالجة الاستثناءات وستعمل دائمًا بغض النظر عما يحدث في معالجات الاستثناءات.
أي:
- ما إذا حدث استثناء أم لا
- حتى إذا تم استدعاء "عودة" في الكتل الأخرى.
- حتى إذا تم إنهاء البرنامج النصي في الكتل الأخرى
لذا ، إذا كان لدينا رمز نريد تشغيله في جميع المواقف ، فإن الحظر في النهاية هو رجلنا. تستخدم هذه الكتلة في الغالب لعمليات التنظيف مثل إغلاق الملفات.
ضع في اعتبارك مثال كود Python أدناه
def readFile(file_path): try: openFile = open(file_path,'r') # Open a file as read-only print(openFile.readline()) # Read first line of file content except FileNotFoundError as ex: print(ex) finally: print("Cleaning...") openFile.close() if __name__ == '__main__': filePath = './text.txt' readFile(filePath)
يحاول هذا الرمز فتح وقراءة الملف text.txt في دليله الحالي. إذا كان الملف موجودًا ، فسيقوم برنامجنا بطباعة السطر الأول من الملف ثم سيتم تشغيل الكتلة الخاصة بنا وإغلاق الملف.
لنفترض أن لدينا ملفًا يسمى text.txt في الدليل حيث ملف البرنامج هذا هو ويحتوي على Hello. إذا قمنا بتشغيل البرنامج ، فسنحصل على الإخراج
تم اختيار هذا المثال عن قصد لأنني أردت منا معالجة مشكلة صغيرة قد تحدث عند إغلاق الملفات في النهاية- block.
إذا لم يكن الملف موجودًا ، فسيتم رفع الاستثناء FileNotFoundError ولن يتم تعريف المتغير openFile ولن يكون ملفًا هدف. ومن ثم ، فإن محاولة إغلاقها في الكتلة النهائية ستثير استثناء UnboundLocalError وهو فئة فرعية من NameError .
هذا يشير بشكل أساسي إلى أننا نحاول الإشارة ال