
פרק קודם:
JSON פייתון 12.2 - קבצימה נלמד
- מהי שגיאה
- איך מטפלים בשגיאות
- איך זורקים שגיאות
- בדיקת שגיאה מול בדיקת קלט
- תבניות לטיפול בשגיאות
מהי שגיאה?



מהי שגיאה?
זוהי הודעה של המערכת שקרה משהו בלתי צפוי או שאיננו יכול לקרות.
עד פרק 13 כנראה שנתקלתם בכאלו שגיאות, למשל מה ייקרה אם תנסו לחלק ב-0?
זה נניח משהו שאיננו יכול לקרות ולכן המערכת “תזרוק” לכם שגיאה שיש לכם חלוקה ב-0.
אילו עוד סיטואציות במחשב יכולות “לזרוק” שגיאות? הכוונה לדברים בלתי צפויים או שאינם יכולים לקרות.
רשמו כמה לפני שאתם ניגשים להמשך הקריאה!
- קובץ שאיננו קיים
- זיכרון נגמר
- לא הצליח להתחבר לשרת
- לא הצליח לסיים את הפעולה
שגיאות בפייתון
יש שגיאות שהמערכת נותנת לנו
לעומת שגיאות שהמערכת נותנת לנו - בפייתון קיימים שגיאות שניתן לצפות שהן יכולות לקרות.
כמה דוגמאות:
קובץ לא קיים
במידה ונכתוב קוד שקורא קבצים, יכול לקרות מצב שקובץ לא יהיה קיים:
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] |
איזה try-except ייטפל בבעיה?
אחד מהחסרונות של try-except
הוא השימוש בשגיאות ספציפיות כמנגנון טיפול.
קראו את קטע הקוד הבא וחשבו לעצמכם מה יודפס:
1 | def A(): |
אם אמרתם C Exception
אז צדקתם!
למה? בתוך B
יש לנו try-except
אז איפה הבעיה?
הבעיה היא בסוג השגיאה שאנו תופסים - except ValueError as e
.
מכיוון ש-B
תופס שגיאה מסוג ValueError
ולנו יש שגיאה של IndexError
.
אז רק פונקציית C
תתמודד עם השגיאה.
לפי הניסיון שלי לרוב מה שאני רואה זה טיפול בשגיאות ספציפיות בתוך פונקציות וטיפול כללי מחוץ לפונקציה.
אז קטע הקוד היה נראה כמו משהו כזה:
1 | def A(): |
בסוף הפוסט נדון בהרחבה יותר מה עדיף.
תרגיל
- תטפלו בשגיאה מסוג “קובץ לא קיים”.
1 | file = open('C:\\text.txt','rt') |
- בהינתן פונקציה שמבצעת חלוקה.
תוסיפוtry-except
לפונקציה כך שבכל מקרה שגוי נחזיר 0 במקום לזרוק שגיאה.
1 | def unsafe_divide(a,b): |
פתרונות
1 | try: |
1 | def safe_divide(a, b): |
איך זורקים שגיאות
הטרמינולוגיה לשגיאות היא לזרוק
או לעלות
שגיאה.
מכיוון שבשפות אחרות המילה שמתשמשים בה היא 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): |
שימו לב למה שהפונקציה מחזירה - היא מחזירה שני משתנים!
הראשון הוא התוצאה שלנו והשני האם הצלחנו או לא.
התבנית הזו טובה לנו אם לא אכפת לנו מהשגיאה אלה רק מהתוצאה -האם יש לנו מספר או לא.
קוד שגיאה
תבנית קוד השגיאה מונעת שימוש בשגיאות קלאסיות דמויי raise Exception()
.
במקום לזרוק שגיאה אנו מחזירים מספר המייצג מה השגיאה אומרת.
גם במצב תקין נחזיר מספר - בדרך כלל זה 0 המייצג שהכל בסדר.
לעיתים בערך החזרה חיובי בלבד, למשל גודל קובץ, נוכל להשתמש במספר כדי לייצג שגיאה.
למשל אם הגודל חוזר -1
אז קרתה שגיאה בקריאת הקובץ.
פרוטוקול הווב HTTP משתמש בערכי קוד לבדיקת התוצאה.
מה הרציונל?
raise
קופץ מחוץ לפונקציות כך שלעיתים נקפוץ מעל קטעי קוד שלמים כשאנו בודקים את התנאי שתופס.
1 | def A(): |
- שימוש בערך החזרה מספרי יכול לעזור לבנות קוד יותר רובסטי על מנת שהמתכנת לא ייצטרך להכיר כל שגיאה ושגיאה פנימית של המערכת.
ההבדל העיצובי
לתמושת לבכם, החלק הזה יכול להיות מורכב אז אם אתם לא מבינים אותו לא נורא, תשלימו את סדרת הפוסטים על פייתון ותחזרו לפה אחר כך כשיש לכם יותר הבנה.
השאלה המהותית ששואלים בהתחלה זה - “אוקיי יש לנו כמה דרכים, מהי הדרך הנכונה?”
אז התובנה הראשונה שלי זה - אין דרך נכונה - העיקר שתהיו עקביים בדרך שלכם.
יתרונות וחסרונות try-except
הפתרון קל מאוד לשימוש.
ניתן להרחיב בצורה פשוטה למגוון רחב של שגיאות.
קוד יותר רובסטי - איננו נוטה להיכשל.
לעומת זאת, הקוד הופך ליותר מסורבל.
פחות קריא כי מוסיף הרבה רווחים.
מלא ב-boilerplate
כי זה להוסיף בלוקים כמעט בכל פונקציה.
לעיתים לא ניתן לשימוש חוזר.
יתרונות חסרונות ב- tryXXX
מוסיף פונקציה יותר פשוטה לשימוש.
ההוספה מתמקדת בתוצאה - האם הצלחתי או לא.
יותר קריא.
לעומת זאת, הקוד בולע שגיאות.
לא ניתן ליישם את התבנית הזו על כל פונקציה.
עובד בעיקר על קוד שהוא פשוט, כמו המרת מספר.
מתאים לקוד בלופים.
הפונקציה מצריכה 2 ערכים להחזרה ולא אחד.
והדבר העיקרי שהוא חסרון בו הוא שצריך להוסיף הרבה משפטי תנאי.
יתרונות וחסרונות ב-קוד שגיאה
תבנית שקיימת זמן רב.
ספריות api
ברמה נמוכה נוטות להשתמש בו.
אפשר לייצג ערך החזרה וקוד שגיאה במספר אחד - למשל בדוגמת גודל קובץ שיכול להיות גם -1
.
ערך אחד שמייצג כמה דברים יכול להיות מבלבל.
צריך לשמור בצד איפשהו את ערכי השגיאה ולפנות אליהם.
מתאים מאוד לקוד יותר low-level
.
למשל - בשימוש בביטים ניתן לחלק בייט אחד ל2 חלקים, ה4 ביטים הראשונים יהיה הערך,
וה-4 ביטים האחרונים ייצגו 4 שגיאות שונות שיכולות לקרות.
למשל:
1 | def SaveFile(lines, path): |
כאן מימשנו את שלושת התבניות.
פונקציה ה-save
מחזירה error-code
ושאר הפונקציות מממשות try-except
עם ה-tryXXX
.
אם חושבים על הקוד הזה בצורה של שכבות:

בכל שכבה כזו נצטרך להחליט אם השכבה למעלה רוצה להכיר את השגיאות הפנימיות או לא.
למשל - האם ברמת המשתמש הוא צריך לדעת שקובץ לא קיים.
יכול להיות שלא, כי התוכנה יכולה ליצור קובץ במידה והוא לא קיים או להשתמש בשם קובץ אחר.
לעיתים נרצה להציג משהו פשוט כמו “משהו השתבש” ולתת למתכנתים להבין מהי השגיאה הפנימית.
אלו תבניות מפורסמות על מנת שהמשתמש לא יתבלבל עם הרבה מידע טכני.
מהסיבה הזו לעיתים נרצה רק להציג למשתמש שגיאה כמו “כן או לא”.
תרגילים
- עליכם לבנות מחשבון פשוט התומך בחיבור, חיסור כפל או חילוק של מספרים של שני מספרים.
ניתן לקבל כקלט:
1+1
2-3
10*2
99/3
עליכם לטפל בשגיאות שיכולות להיווצר - על המחשבון להיות רובסטי (Robust).
השתמשו בתבנית TryXXX.
להלן קטע קוד שייבדוק את המחשבון שלכם:
1 | def TryCalculate(userInput): |
כחלק מקוד שמטפל באלוקציות -
Allocations
- אתחול זכרון, עלינו לבנות פונקציות שעוזרות להקצות אותו.
תבנו פונקציה שמקצה מערך בגודל מסוים - והאם הגודל עובר את 1024 - על הפונקציה לזרוק שגיאה!כתבו פונקציה
read_file
המקבלת נתיב לקובץ ומחזירה:
0 ואת תוכן הקובץ במידה והכל בסדר.
אם הקובץ לא קיים אז להחזיר 1 והסיבה לזה.
אם אין לכם הרשאות יש להחזיר 2 עם הסיבה.
אם זו סיבה אחרת וזה לא הצליח להחזיר 3.
שימו לב, ניתן להחזיר כמה פרמטרים בעזרת:
1 | def myFunc(): |
זה נקרא desctruction
.
1 | def TryParseInt(stringToParse): |
1 | def AllocateArray(size): |
1 | import os |

בפרק הבא נבנה קוד כמו אמאזון - בחבילות!
פייתון 14 - מודולים