פרק קודם:
JSON פייתון 12.2 - קבצימה נלמד
- מהי שגיאה
- איך מטפלים בשגיאות
- איך זורקים שגיאות
- מה כדאי, לבדוק או לשגות?
מהי שגיאה?
מה זה שגיאה?
זו הודעה של המערכת למתכנת שמשהו בלתי צפוי קרה.
שגיאות בפייתון
לעומת שגיאות שהמערכת נותנת לנו - בפייתון קיימים שגיאות שניתן לצפות שהן יכולות לקרות.
כמה דוגמאות:
קובץ לא קיים
במידה ונכתוב קוד שקורא קבצים, יכול לקרות מצב שקובץ לא יהיה קיים:
1 | with open('c:\\fileDoesntExists.txt','rt') as file: |
שגיאה בהמרה
תקינות מידע הוא נושא מאוד רגיש בתכנות, במיוחד כשמדובר בקלט של משתמש!
קלט לא תמיד יהיה תקין או מה שאנחנו מצפים לו.
למשל בהמרה של מספר שאנחנו מניחים שהמידע הוא מספר:
1 | a = "abc" |
תרגיל
תחשבו על דוגמאות נוספות בפייתון שזורקות שגיאות
שגיאה בתחביר!
נסו להריץ את הקוד הזה:1
2
3a = True
if a
passאינדקס מחוץ לגודל הרשימה
אם גודל הרשימה שלנו בלתי צפוי יכול לקרות מצב שבו ניגש לרשימה עם אינדקס גדול מדי.1
2list = [1,2,3]
list[4]RecursionError
אם אנחנו טועים ברקורסיה ייקרה מצב שבו היא לא תצא ותקרה שגיאה.1
2
3
4def Function():
Function()
Function()
מה קורה אם אנחנו לא מטפלים בשגיאות?
קוד שלא נמצא מוגן יפסיק לרוץ כי שגיאה כזו היא בטרמינולוגיה נקראת “שגיאה פטאלית” - Fatal Error
.
שגיאה שהתוכנה שלנו לא יכולה להתאושש ממנו ולכן היא חייבת להיכבה.
במערכות הפעלה קיימות גם שגיאות כאלו כמו ה-Blue Screen
שיש בווינדוס.
במקרה שהתוכנה שלנו מפסיקה לרוץ ניתן לומר שהיא “קורסת” או “מפסיקה את הריצה”.
איך לטפל בשגיאות
ניתן לטפל בשגיאה בעזרת 3 מילים שמורות: try
, except
, finally
.try
- מגדיר קטע קוד מוגן משגיאות.except
- מטפל שגיאה מסוימת או כלליתfinally
- מה קורה בסופו של דבר
טיפול בשגיאות
ניתן לטפל בשגיאות באופן כללי ללא התייחסות לסוג השגיאה בעזרת קטע הקוד:
1 | try: |
כל מה שבתוך ה-try
יהיה מוגן משגיאות.
במקרה ותקרה שגיאה מכל סוג שהיא, קטע הקוד בתוך ה-except
ירוץ.
טיפול פרטני בשגיאה
עבור שגיאה פרטנית ניתן למנות את סוג השגיאה,
למשל בשגיאה הקודמת יש לנו שגיאה מסוג IndexError."as e"
נותן שם לשגיאה שנוכל להשתמש בו בתור משתנה.
1 | try: |
finally - גם אם לא קרתה שגיאה
ניתן להשתמש ב-finally
גם אם לא קרתה שגיאה:
1 | try: |
ניתן להשתמש בקוד שנמצא ב-finally
כדי לבצע עבודות ניקוי במידה ורוצים.
1 | list = [1,2,3,4,5] |
finally
לא מטפל בשגיאות בפני עצמו:
1 | list = [1,2,3,4,5] |
תרגיל
- תטפלו בשגיאה מסוג “קובץ לא קיים”.
1
file = open('C:\\text.txt','rt')
איך זורקים שגיאות
הטרמינולוגיה לשגיאות היא לזרוק
או לעלות
שגיאה.
מכיוון שבשפות אחרות המילה שמתשמשים בה היא throw
.
ובפייתון המילה השמורה הזו היא raise
.
לזרוק שגיאה כללית
1 | a = input('Enter your username: ') |
אם שם המשתמש שלנו קצר מדי אז נזרוק שגיאה מתאימה עם ההודעה: Username ____ is too short
.
שגיאה בתוך שגיאה
קיימות לנו סיטואציות שאנחנו רוצים להתערב כאשר יש שגיאה אך לא לטפל בה.
למשל אם אנחנו כותבים קטע קוד שכותב את השגיאות לקובץ:
1 | def WriteErrorToLog(error): |
רק התערבנו בתהליך מבלי לשנות אותו - השגיאה עדיין קיימת ומי שמשתמש בקוד שלנו ייצטרך לטפל בה.
זה מאוד משפיע על תהליך כתיבת קוד נפרד - אם צוות כותב את הקוד לטיפול בכתיבה לקבצי לוג וצוות אחר כותב את הקוד לטיפול בשגיאות.
אסור “לבלוע” שגיאות שאנחנו לא מטפלים בהם
למשל קטע הקוד הזה פסול:
1 | def DoSomethingBig(): |
הקוד מסתיר שגיאות ותופס את כל סוגי השגיאות.
אם הפונקציה DoSomethingBig
תשתנה אנחנו נסתיר שגיאות ונגרום לבאגים בלתי צפויים.
הדבר הכי נורא בתכנות זה דברים בלתי צפויים!
לכן אם אתם לא בטוחים, עדיף לתת לשגיאה להיזרק במקום לתפוס שגיאה שאתם לא יודעים איך לצאת ממנה.
לטפל בשגיאה או לבדוק את הקלט?
ההחלטה אם לטפל בשגיאות או לבדוק את הקלט היא החלטה עיצובית
טיפול בשגיאות - ותלוי באיזה סוג של שגיאות זוהי החלטה עיצובית שמשפיעה על הריצה של הקוד שלנו.
במידה ואנחנו יודעים בוודאות מה יקרה אם תקרה שגיאה מסוימת ואיך להתנהג מולה - אנחנו צריכים להוסיף את הטיפול בשגיאה הזו.
לפעמים טיפול בשגיאה יכול לסבך לנו את הקוד ואנחנו רוצים להשאיר אותו פשוט.
הדוגמא הקלאסית היא חלוקה ב-0.
1 | # Ignore the ValueError that can be thrown from int() |
אם המשתמש יכניס 0
- הקוד הזה יפסיק לרוץ בגלל שגיאה.
ניתן לטפל בשגיאה הזו בשני דרכים:
לטפל בשגיאה
1
2
3
4
5
6
7a = int(input('Pick a divider'))
b = 0
try:
b = 5 / a
print(f"Result is {b}")
except:
print(f"Error a is {a}")לבדוק אם זה 0.
1
2
3
4
5
6a = int(input('Pick a divider'))
if a == 0:
print("Cannot divide by 0")
else:
b = 5 / a
print(f"Result is {b}")
ההחלטה אם ללכת על גישה בטוחה יותר ולבדוק ולידציה או לתפוס שגיאות בהנחה שהמידע לא נכון היא על ידי כותב/ת הקוד.
Try Pattern
תבנית נפוצה שבולעת שגיאות היא ה - TryXXX pattern
.
בתבנית הזו אנחנו מנסים לבצע פעולה ומחזירים שני ערכים:
- אם הצלחנו או לא.
- את ערך ההחזרה שרצינו להחזיר.
1 | def TryParseInt(stringToParse): |
שימו לב למה שהפונקציה מחזירה - היא מחזירה שני משתנים!
הראשון הוא התוצאה שלנו והשני האם הצלחנו או לא.
התבנית הזו טובה לנו אם לא אכפת לנו מהשגיאה אלה רק מהתוצאה -האם יש לנו מספר או לא.
ההבדל העיצובי
ההבדל בין תבנית Try - True/False
ל-try except
הוא כמות הבדיקות.
בד”כ כשמחזירים משתנה בוליאני שאומר לנו אם הפעולה הצליחה או לא, נרצה לעלות את התוצאה הזו למעלה בהיררכיה של הפונקציות.
למשל:
1 | def TrySaveFile(lines, path): |
בדוגמא הזו אין לנו try-except
ברמה העליונה אך הסתרנו את זה ברמה התחתונה.
מבחינה עיצובית זה אפשרי אך לא תמיד כדאיי - ההסתרה של השגיאות פה מעלה לנו משהו שנקרא Robustness
- העמידות של תוכנה בשגיאות,
אך גם בגלל ההסתרה אנחנו מורידים את ההבנה של התוכנה שלנו - כל שגיאה תהיה פנימית.
לפעמים אנחנו רוצים לעלות שגיאות למעלה אך לפעמים אנחנו רוצים לטפל בהם ברמה שלנו וזה מה שמבדיל בין השכבות.
כל מקטע בקוד מטפל בסוג המשתנים שלו וסוג השגיאות שלו, לפעמים כדאי לבחור לטפל בשגיאה ברמה הפרטנית.
שכבת האפליקצייה לא תדע מה זה אומר “חסר קובץ מטמון”.
לכן עלינו לטפל בשגיאה ברמה הפנימית ולהחליט ברמה היותר גבוה - כיצד התוצאה תשפיע.
מתכנתים הם לא קופי קוד - עליהם לדעת באיזו דרך כדאי יותר לבחור
תרגילים
- עליכם לבנות מחשבון פשוט התומך בחיבור, חיסור כפל או חילוק של מספרים של שני מספרים.
ניתן לקבל כקלט:
1+1
2-3
10*2
99/3
עליכם לטפל בשגיאות שיכולות להיווצר - על המחשבון להיות רובסטי (Robust).
השתמשו בתבנית TryXXX.
להלן קטע קוד שייבדוק את המחשבון שלכם:
1 | def TryCalculate(userInput): |
- כחלק מקוד שמטפל באלוקציות - Allocations - אתחול זכרון, עלינו לבנות פונקציות שעוזרות להקצות אותו.
תבנו פונקציה שמקצה מערך בגודל מסוים - והאם הגודל עובר את 1024 - על הפונקציה לזרוק שגיאה!
1 | def TryParseInt(stringToParse): |
1 | def AllocateArray(size): |
בפרק הבא נבנה קוד כמו אמאזון - בחבילות!
פייתון 14 - מודולים