تعلم البرمجة مع Python


الدرس: معالجة الاستثناءات


الصفحة السابقة
في هذا الفصل ، سوف نتناول المفهوم الأخير الذي أعتبره ضروريًا قبل تناول الجزء الخاص بالبرمجة الكائنية ، وقد سميت "الاستثناءات" .

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

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

ما هو استخدامه؟



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


>>> # المثال الكلاسيكي: اختبار القسمة على الصفر
>>> variable = 1/0
Traceback (most recent call last):
  File "", line 1, in 
ZeroDivisionError: int division or modulo by zero
 
دعونا نتناول السطر الأخير. نجد هناك نوعين من المعلومات:
  • ZeroDivisionError : نوع الاستثناء ؛
  • int division or modulo by zero : الرسالة التي ترسلها Python لمساعدتك في فهم الخطأ الذي حدث للتو.
لذلك ، فإن Python تطرح استثناءات عندما تجد خطأً ، إما في الكود (خطأ نحوي ، على سبيل المثال) ، أو في العملية التي تطلب منها القيام بها.

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

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

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


annee = input() # نطلب من المستخدم ادخال السنة
annee = int(annee) # نحاول تحويل السنة إلى عدد صحيح
 
أخبرتك أنه إذا قدم المستخدم هنا قيمة لا يمكن تحويلها إلى عدد صحيح (حرف على سبيل المثال) ، فإن البرنامج سيتعطل. في الواقع ، يطرح استثناء وتوقف Python تنفيذ البرنامج. إذا اختبرت البرنامج بالنقر المزدوج مباشرة في المستكشف ، فسيتم إغلاقه على الفور (في الواقع ، يعرض الخطأ ولكنه يغلق على الفور).

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

الحد الأدنى من شكل كتلة try



سوف نتحدث هنا عن الكتلة try . سنضع بالفعل التعليمات التي نريد اختبارها في الكتلة الأولى والتعليمات التي سيتم تنفيذها في حالة حدوث خطأ في كتلة أخرى. بدون مزيد من اللغط ، ها هي البنية:


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

annee = input()
try: # نحاول تحويل العام الى عدد صحيح
    annee = int(annee)
except:
    print("خطأ في تحويل السنة")
 
يمكنك اختبار هذا الكود عن طريق تحديد عدة قيم مختلفة للمتغير annee، مثل "2010" أو "year2010" .

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

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

شكل أكثر اكتمالا



سوف نتعلم كيفية إكمال كتلة لدينا try . كما أشرت أعلاه ، يجب تجنب النموذج الأدنى لعدة أسباب.

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

تنفيذ باستثناء كتلة لنوع معين من استثناء


في المثال الذي رأيناه أعلاه ، نفكر فقط في نوع واحد من الاستثناءات التي يمكن طرحها: النوع ValueError، الذي قد يخون خطأ التحويل. دعنا نرى مثالاً آخر:


try:
    resultat = numerateur / denominateur
except:
    print("حدث خطأ ... ما هو؟?")
 
هنا ، من المحتمل أن تحدث عدة أخطاء ، كل منها يثير استثناءً مختلفًا.
  • NameError : أحد المتغيرات numerateur أو denominateur لم يتم تعريفه (غير موجود). إذا جربت العبارة في المترجم print(numerateur) دون تحديد المتغير numerateur، فستحصل على نفس الخطأ.
  • TypeError : أحد المتغيرات numerateur أو denominateur لا يمكن قسمته (سلاسل الأحرف لا يمكن تقسيمها أو تقسيم الأنواع الأخرى ، على سبيل المثال). تم طرح هذا الاستثناء لأنك تستخدم عامل القسمة "/" على الأنواع التي لا تُقسم.
  • ZeroDivisionError : مرة أخرى! إذا كانت denominateur تساوي 0 ، فسيتم رفع هذا الاستثناء.
هذا التعداد ليس قائمة شاملة لجميع الاستثناءات التي يمكن طرحها أثناء تنفيذ هذا الكود. وهي موجودة بشكل أساسي لتظهر لك إمكانية حدوث العديد من الأخطاء في التعليمات (وهي أكثر وضوحًا على كتلة مكونة من عدة تعليمات) وأن النموذج الأدنى يعترض كل هذه الأخطاء دون تمييزها ، والتي يمكن أن تكون مشكلة في حالات معينة.

يتم لعب كل شيء على السطر except . بين هذه الكلمة الرئيسية والنقطتين ، يمكنك تحديد نوع الاستثناء الذي تريد معالجته.


try:
    resultat = numerateur / denominateur
except NameError:
    print("لم يتم تعريف متغير البسط أو المقام.")
 
هذا الكود يتعامل فقط مع الحالة التي NameError يتم فيها طرح استثناء . يمكنك التعرف على أنواع أخرى من الاستثناءات عن طريق إنشاء كتل أخرى except متتالية:

try:
    resultat = numerateur / denominateur
except NameError:
    print("لم يتم تعريف متغير البسط أو المقام.")
except TypeError:
    print("متغير البسط أو المقام له نوع غير متوافق مع القسمة.")
except ZeroDivisionError:
    print("متغير المقام يساوي 0.")
 
هذا أفضل أليس كذلك ؟

هيّا هذا الأخيرًا!

يمكنك التقاط الاستثناء وعرض رسالته باستخدام الكلمة الأساسية as التي رأيتها بالفعل في سياق آخر (نعم ، تذكر استيراد الوحدات النمطية).


try:
    #  test
except type_de_l_exception as exception_retournee:
    print("Voici l'erreur :", exception_retournee)
 
في هذه الحالة ، exception_retournee يتم إنشاء متغير بواسطة Python إذا تم طرح استثناء من النوع المحدد في الكتلة try .

أنصحك دائمًا بتحديد نوع الاستثناء بعد except (دون بالضرورة التقاط الاستثناء في متغير ، بالطبع) . أولاً ، يجب ألا تستخدمه try كطريقة لاختبار أي جزء من التعليمات البرمجية. من المهم أن تحافظ على أقصى قدر من التحكم في التعليمات البرمجية الخاصة بك. هذا يعني أنه في حالة حدوث خطأ ، يجب أن تكون قادرًا على توقعه. في الممارسة العملية ، لن تذهب إلى حد اختبار ما إذا كان أي متغير موجودًا ، يجب أن يكون لديك حد أدنى من الثقة في الكود الخاص به. ولكن إذا كنت أمام قسمة وكان للمقام قيمة 0 ، فضع القسمة في كتلة try وحدد ، بعد except، نوع الاستثناء المحتمل حدوثه (ZeroDivisionError في هذا المثال) .

إذا قمت بتبني النموذج الأدنى (أي except بدون تحديد نوع الاستثناء الذي يمكن أن يحدث في الكتلة try ) ، فسيتم التعامل مع جميع الاستثناءات بنفس الطريقة. وعلى الرغم exception = erreur من أن هذا ليس هو الحال في معظم الأحيان. على سبيل المثال ، تطرح Python استثناءً عندما تريد إغلاق برنامجك بالاختصار CTRL+ C . هنا قد لا ترى المشكلة ولكن إذا كانت الكتلة الخاصة بك try في حلقة ، فلن تتمكن من إيقاف البرنامج باستخدام CTRL+ C، حيث سيتم التعامل مع الاستثناء بواسطة ملف except .

لذلك أنصحك دائمًا بتحديد نوع الاستثناء المحتمل بعد except . يمكنك بالطبع إجراء بعض الاختبارات في مترجم أوامر Python لإعادة إنتاج الاستثناء الذي تريد معالجته وبالتالي معرفة نوعه.

الكلمات الرئيسية else و finally


هاتان الكلمتان الأساسيتان ستسمحان لنا ببناء كتلة try أكثر اكتمالاً.

الكلمة ELSE

لقد رأيت هذه الكلمة الرئيسية من قبل وآمل أن تتذكرها. في الكتلة try ، else سيسمح بتنفيذ إجراء إذا لم يحدث خطأ في الكتلة. هذا مثال صغير:

try:
    resultat = numerateur / denominateur
except NameError:
    print("لم يتم تعريف متغير البسط أو المقام.")
except TypeError:
    print("متغير البسط أو المقام له نوع غير متوافق مع القسمة.")
except ZeroDivisionError:
    print("متغير المقام يساوي 0.")
else:
    print("النتيجة التي تم الحصول عليها هي ", resultat)
 
في الواقع ، نحن نستخدم القليل جدًا else . تفضل معظم برامج التشفير وضع السطر الذي يحتوي على المقطع print مباشرة في الكتلة try . من ناحيتي ، أجد أنه من المهم التمييز بين الكتلة try وما يتم عمله بعد ذلك . من print غير المحتمل أن ينتج عن سطر أي أخطاء ، فلا داعي لوضعه في الكتلة try .

الكلمة FINALLY

Finally تسمح لك بتنفيذ التعليمات البرمجية بعد الكتلة try، بغض النظر عن نتيجة تنفيذ الكتلة المذكورة . بناء الجملة بسيط للغاية:

try:
    # اختبار (اختبارات) التعليمات
except type_de_l_exception:
    # العلاج في حالة الخطأ
finally:
    # تم تنفيذ التعليمات سواء كانت هناك أخطاء أم لا
 
ألا يعود الأمر إلى نفس الشيء إذا وضعنا الكود بعد الكتلة مباشرة؟
ليس كليا. Finally يتم تنفيذ الكتلة في جميع الحالات. حتى إذا عثرت Python على تعليمات return في الكتلة الخاصة بك except على سبيل المثال ، فإنها ستنفذ الكتلة finally .

مكافأة صغيرة: الكلمة الأساسية PASS

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

>>> try:
...     1/0
...
  File "", line 3

    ^
SyntaxError: invalid syntax
 
هناك كلمة أساسية يمكن استخدامها في هذه الحالة. اسمها pass وصياغتها سهلة الاستخدام للغاية:

try:
    # اختبار (اختبارات) التعليمات
except type_de_l_exception: # لا يجب أن يحدث شيء في حالة الخطأ
    pass
 
أنا لا أشجعك بشكل خاص على استخدام هذه الكلمة الرئيسية ، لكنها موجودة ، وأنت تعرفها الآن.

Pass ليست كلمة رئيسية خاصة بالاستثناءات: يمكن العثور عليها أيضًا في ظروف أو وظائف نريد تركها فارغة.

هنا رأينا الأساسي. لا يزال يتعين علينا تقييم التأكيدات ومعرفة كيفية طرح استثناء (سيكون سريعًا جدًا).

التأكيدات



التأكيدات هي طريقة بسيطة للتأكد ، قبل المتابعة ، من تلبية الشرط. عادة ما يتم استخدامها في كتل try … except .

دعونا نرى كيف يعمل: لهذه المناسبة سوف نكتشف كلمة رئيسية جديدة (أخرى) assert،. تركيبها كما يلي:


assert test
 
إذا أعاد الاختبار True، يستمر التنفيذ بشكل طبيعي. خلاف ذلك ، AssertionError يتم طرح استثناء . دعونا نرى مثالاً:

>>> var = 5
>>> assert var == 5
>>> assert var == 8
Traceback (most recent call last):
  File "", line 1, in 
AssertionError
>>>
 
كما ترى ، يعمل السطر 2 بشكل جيد ولا يطرح أي استثناءات. نختبر إذا var == 5 . هذا هو الحال ، وبالتالي فإن الاختبار صحيح ، لا استثناء.

في السطر التالي ، ومع ذلك ، فإن الاختبار هو var == 8 . هذه المرة الاختبار غير صحيح ويظهر استثناء من النوع AssertionError .

ما هو ، بشكل ملموس؟

في اختبار البرنامج ، إذا كانت السنة سنة كبيسة ، فقد نرغب في التأكد من أن المستخدم لا يدخل سنة أقل من أو يساوي 0 على سبيل المثال. مع التأكيدات ، من السهل جدًا القيام بذلك:


annee = input("أدخل سنة أكبر من 0:")
try:
    annee = int(annee) 
    assert annee > 0
except ValueError:
    print("لم تقم بإدخال رقم.")
except AssertionError:
    print("السنة التي تم إدخالها أقل من أو تساوي 0.")
 

رفع استثناء



هممم ... أستطيع رؤية الوجوه المتشككة من هنا (لا لا ، لا تختبئ!). ربما تتساءل عن سبب قيامك بعمل بايثون من خلال طرح الاستثناءات. بعد كل شيء ، وظيفتك من الناحية النظرية هي منع برنامجك من الانهيار.

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

نستخدم كلمة رئيسية جديدة لطرح استثناء… الكلمة الأساسية raise .


raise TypeDeLException("message ")
 
لنأخذ مثالًا صغيرًا ، لا يزال حول برنامجنا bissextile . سنطرح استثناءً للنوع ValueError إذا أدخل المستخدم سنة سلبية أو صفر.

annee = input()# يدخل المستخدم العام
try:
    annee = int(annee) 
    if annee<=0:
        raise ValueError("السنة التي تم إدخالها بالسالب أو الصفر ")
except ValueError:
    print("القيمة التي تم إدخالها غير صالحة (قد تكون السنة سالبة).")
 
ما فعلناه للتو يمكن تحقيقه دون استخدام الاستثناءات ، ولكن كان الهدف الأساسي هو إظهار بناء الجملة لك في سياق حقيقي. هنا ، نطرح استثناءًا نلاحظه على الفور أو تقريبًا ، وبالتالي فإن الفائدة محدودة. بالطبع ، ليس هذا هو الحال في معظم الأحيان.

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

باختصار


  • يمكننا اكتشاف الأخطاء (أو الاستثناءات) التي يثيرها الكود باستخدام الكتل try except .
  • بناء جملة التأكيد هو assert test .
  • تطرح التأكيدات استثناءً AssertionError إذا فشل الاختبار.
  • يمكنك طرح استثناء باستخدام الكلمة الأساسية raise متبوعة بنوع الاستثناء.