3.4 טיפול בשגיאות הרצאה
שגיאות¶
- שגיאות הן בעיות לא צפויות שיכולות לקרות בזמן שהתוכנה רצה ולגרום לה לקרוס.
- סוגי שגיאות: בפייתון יש המון סוגים של שגיאות, כגון:
TypeError,ValueError,ZeroDivisionError, וכו.
-
בדוגמה למעלה הקוד יזרוק שגיאת
ZeroDivisionError. -
סטאק טרייס: (stack trace) כאשר שגיאה קורת, פייתון מייצרת סטאק טרייס- שזה דוח שמציין את הדברים הבאים:
- איזה שגיאה קרתה,
- איזה שורת קוד גרמה לה לקרות,
- באיזה פונקציה השורה נמצאת,
- מי קרא לפונקציה וכך הלאה.
זה עוזר לנו למצוא את הבעיה ואת הסיבה שהשגיאה קרתה.
-
הריצו את הקוד הבא בעצמכם ותראו את הפלט.
-
רייז: (raise) אנחנו יכולים להשתמש ב
raiseבפייתון כדי לזרוק שגיאות באופן מכוון, זה שימושי כשאנחנו רוצים שהתוכנה תחזיר שגיאה כאשר קורה משהו מסויים. - למשל, אם פונקציה שכתבנו קיבלה פרמטר לא צפוי, אולי נרצה להחזיר שגיאה יזומה.
- הדרך הטובה ביותר לדאוג לכך שנעביר פרמטרים נכונים לפונקציות שלנו ולא נצור בעיות היא באמצעות זריקת שגיאות:
- הקוד למעלה בודק האם הפרמטרים שהועברו לפונקציה הם מספרים, ושמכנה הוא לא 0.
- מטרת הבדיקות האלו היא שהפונקציה לא תזרוק שגיאה, אלה אם כן הפרמטרים שהועברו לא נכונים.
הבעיה בשגיאות¶
- שגיאות מקריסות את התוכנה, תוכנה טובה לא קורסת.
- פתרון: "טיפול בשגיאות" או באנגלית "exception handling" זה גישה תכנותית למנוע שגיאות.
טיפול בשגיאות¶
- כאשר אנחנו כותבים פונקציות שעלולת לזרוק שגיאות, נכתוב פונקציות שמטרתם לתפוס את השגיאות ולטפל בהם במקום להקריס את התוכנה. נעשה זאת באמצעות
tryו -execpt
def divide_numbers(a, b): return a / b def divide_operation(first_num, second_num): try: result = divide_numbers(first_num, second_num) # This will maybe raise a ZeroDivisionError except ZeroDivisionError: print("Cannot divide by zero!") return print("Operation succeeded") return result divide_operation(10, 0) # Program won't crash :) - הפונקציה
divide_numbersהיא פונקציה שעשוייה לקרוס ולהחזיר שגיאות, אז נעטוף אותה עם הפונקציהdivide_operationשמטרתה לקרוא לה, ולדאוג שאם היא תחזיר שגיאה אז לטפל בה במקום להקריס את התוכנה. -
הבלוק קוד שנמצא ב
tryזה בלוק שאם קורת בו שגיאה מסוגZeroDivisionErrorהבלוקexceptיורץ. -
מה אם נרצה לתפוס עוד סוגים של שגיאות שעלולות לקרות? למשל ValueError? (כאשר הוכנס מחרוזת לפונקציה), נוכל לעשות כמה בלוקים של except.
def divide_numbers(a, b): return a / b def divide_operation(first_num, second_num): try: result = divide_numbers(first_num, second_num) # This will maybe raise a ZeroDivisionError except ZeroDivisionError: print("Cannot divide by zero!") return except ValueError: print("Can only divide numbers!") return print("Operation succeeded") return result divide_operation("sdsda", 3) # Program won't crash :) -
אם השגיאה תהיה
ZeroDivisionErrorאז הבלוק של הexcept הראשון יורץ, אם השגיאה תהיהValueError, אז הבלוק של הexcept השני יורץ. -
ומה אם תקרה שגיאה כללית שאנחנו לא בהכרח מכירים? איך נוכל לתפוס כל שגיאה אחרת? באמצעות
except Exception
def divide_numbers(a, b): return a / b def divide_operation(first_num, second_num): try: result = divide_numbers(first_num, second_num) # This will maybe raise a ZeroDivisionError except ZeroDivisionError: print("Cannot divide by zero!") return except ValueError: print("Can only divide numbers!") return except Exception as e: print(f"An error occurred: {e}") return print("Operation succeeded") return result divide_operation(10, 0) # Program won't crash :) - למעשה כשאנחנו עושים except Exception, אנחנו תופסים כל שגיאה בעולם שעלולה לקרות, כי כל הexception-ים הם נכללים בתוך Exception.
-
סיכום: אנחנו יכולים לכתוב קוד שמבצע טיפול בכל סוג של שגיאה.
-
במידה ונרצה להריץ בלוק של קוד אחרי
tryו -execptשירוץ לא משנה אם קרתה שגיאה או לא, נשתמש בfinally
def divide_numbers(a, b): return a / b def divide_operation(first_num, second_num): try: result = divide_numbers(first_num, second_num) # This may raise a ZeroDivisionError except ZeroDivisionError: print("Cannot divide by zero!") return except Exception as e: print(f"An error occurred: {e}") return print("Operation succeed") return result finally: print("Division operation complete.") # This block will always execute, regardless of exceptions result = divide_operation(10, 2) # Operation succeed, Division operation complete. print(result) # Output: 5.0 - לא משנה אם התוכנה קרסה או לא, תמיד הבלוק finally יורץ.
- אפשר לראות שאם לא הייתה שגיאה קיים עוד בלוק של קוד שירוץ, (נמצא בין הexcept Exception ל- finally) אפשר להשתמש ב
elseכדי לתחם אותו בצורה ברורה יותר.
def divide_numbers(a, b): return a / b def divide_operation(first_num, second_num): try: result = divide_numbers(first_num, second_num) # This may raise a ZeroDivisionError except ZeroDivisionError: print("Cannot divide by zero!") except Exception as e: print(f"An error occurred: {e}") else: print("Operation succeed") return result finally: print("function finished.") # This block will always execute, regardless of exceptions result = divide_operation(10, 2) # Operation succeed, Division operation complete. print(result) # Output: 5.0 - שימו לב שהקוד הבא הוא זהה לקוד הקודם.
משפחות של שגיאות¶
- בתמונה הבאה קיימים המון שגיאות בפייתון.

- שימו לב שגם קיימים משפחות של שגיאות: (שגיאה שמצביעה על שגיאות אחרות)
- למשל ArithmeticError הוא כולל בתוכו גם את ZeroDivisionError וגם OverflowError.
- אם נעשה
except ArithmeticErrorאנחנו נתפוס את כל משפחת השגיאות הזו. - בנוסף, אפשר לראות בתמונה שכל השגיאות הם במשפחה של Exception, וזו הסיבה שאם נעשה
except Exceptionאנחנו נתפוס את כל השגיאות.
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
בדיקת קלט vs טיפול בשגיאות¶
אם אתם רוצים לכתוב קוד עם כמה שפחות שגיאות ובאגים, יש שני דרכים.
1. דרך ראשונה - באמצעות טיפול בשגיאה:
def handle_division(number1, number2):
try:
return number1 / number2
except Exception:
print(f"An error occurred.")
return
while True:
number1 = input("enter first number: ")
number2 = input("enter second number: ")
handle_division(number1, number2)
- בדוגמה למעלה וידאתי שהפרמטרים שהועברו לפונקציה
handle_division נכונים באמצעות try, except
- דרך שנייה - באמצעות בדיקת קלט
def handle_division(number1, number2): return number1 / number2 def get_input(): while True: number1 = input("enter first number: ") number2 = input("enter second number: ") if isinstance(number1, int) and isinstance(number2, int): return number1, number2 else: print("numbers can be only integers!") while True: number1, number2 = get_input() handle_division(number1, number2) -
בדוגמה למעלה וידאתי שהפרמטרים שהועברו לפונקציה
handle_divisionנכונים באמצעות תנאים (בדיקת קלט). -
למעשה תמיד עדיף שילוב של שניהם, כך אתם יכולים לוודא שהקוד שלכם יהיה כמה שיותר נכון ובטוח.
def handle_division(number1, number2): try: return number1 / number2 except Exception: print(f"An error occurred.") return def get_input(): while True: number1 = input("enter first number: ") number2 = input("enter second number: ") if isinstance(number1, int) and isinstance(number2, int): return number1, number2 else: print("numbers can be only integers!") while True: number1, number2 = get_input() handle_division(number1, number2) - כך אנחנו גם בודקים את הinput,
- וגם תופסים שגיאות:
- אל תכתבו רק try ו - except, תמיד תכתבו גם בדיקות קלט כמו בדוגמה, אל תסתמכו אף פעם על הקלט שהמשתמש מכניס לתוכנה שלכם.