2.25. טיפול בשגיאות

רוב בעיות זמן הריצה ב-Python צפות כחריגות (exceptions) – דרך בעלת שם ומובנית לדווח שמשהו השתבש. ValueError, TypeError, KeyError, OSError, MemoryError הן כולן דוגמאות; כל אחת היא מחלקה, וזריקה (raising) של אחת מהן עוצרת את הקריאה הנוכחית ומחפשת מטפל בקוד הסובב.

2.25.1. try / except

עטפו בלוק קוד ב-try כדי לתפוס כל חריגה שהוא זורק:

try:
    n = int(input_text)
except ValueError:
    n = 0

אם המרת ה-int(...) נכשלת, השליטה קופצת לבלוק ה-except במקום להפיץ את השגיאה הלאה. אם input_text היה מחרוזת מספר שלם תקפה, בלוק ה-except מדולג.

ל-try יחיד יכולים להיות מספר בלוקי except, שכל אחד תופס סוג שגיאה שונה:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python מתאים לפי הסדר; הראשון שמחלקת החריגה שלו מתאימה מטפל בבעיה. תפיסת Exception (מחלקת הבסיס של כמעט הכל) מטפלת בכל שגיאה; שמרו זאת לשכבה החיצונית ביותר של תוכנית שבה החלופה היא קריסה.

אזהרה

except: חשוף (ללא מחלקה אחרי מילת המפתח) תופס גם את KeyboardInterrupt – החריגה שה-IDE שולח כאשר אתם לוחצים על כפתור ה-stop שלו כדי להפסיק סקריפט רץ. לולאה עטופה ב-except: pass חשוף תבלע את ההפסקה ותמשיך לרוץ, ולא תשאיר דרך לעצור את הסקריפט מלבד הפעלה מחדש של המתח.

העדיפו except Exception: על פני except: חשוף כאשר אתם באמת צריכים לתפוס באופן רחב. KeyboardInterrupt יורש מ-BaseException, ולא מ-Exception, כך ש-except Exception: משאיר את כפתור ה-stop פעיל.

2.25.1.1. בחינת החריגה

כדי לקרוא את ההודעה המצורפת לחריגה, תנו לה שם עם as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

המשתנה שנקשר על ידי as תקף רק בתוך בלוק ה-except.

2.25.2. else ו-finally

לבלוק try יש שתי תוספות אופציונליות.

else רץ רק כאשר ה-try הסתיים בלי לזרוק חריגה:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

הצבת ”מה לעשות כשזה עבד“ ב-else שומרת על בלוק ה-try צר – רק השורה שעלולה להיכשל שייכת ל-try.

finally רץ בסוף לא משנה מה – בין אם ה-try הצליח, זרק וטופל, או זרק ועומד להתפשט:

try:
    do_work()
finally:
    cleanup()
Flow diagram showing the four possible paths through try / except / else / finally: success goes to else then finally; a caught exception goes to except then finally; an uncaught exception goes to finally then propagates.

finally תמיד רץ. else רץ רק במסלול הלא-חריג.

עבור רוב תבניות הקצאה/שחרור, העדיפו מנהל הקשר על פני זוג try / finally – המשאב עצמו מנהל את הניקוי שלו.

2.25.3. חריגות מובנות נפוצות

רשימה קצרה של החריגות שתיתקלו בהן לעיתים קרובות:

  • ValueError – טיפוס נכון, ערך שגוי (bytes([300]) – 300 הוא הטיפוס הנכון, אבל מחוץ לטווח הבייט התקף של 0..255).

  • TypeError – טיפוס שגוי לחלוטין (len(42)).

  • KeyError – מפתח חסר ב-dict.

  • IndexError – אינדקס מעבר לסוף של רצף.

  • AttributeError – גישה לתכונה שאינה קיימת ("abc".foo).

  • OSError – כשל מערכת קבצים או קלט/פלט.

  • MemoryError – אזל ה-heap. בזמן ריצה מוגבל זיכרון, זה יכול לקרות במהלך פעולה רגילה – לא רק במקרים פתולוגיים.