פוסט
TypeError ובדיקת טיפוסים ב-Python
הודעות TypeError נפוצות ב-Python ומה הן אומרות, בדיקת טיפוסים עם type() ו-isinstance(), למה רמזי טיפוסים לא נאכפים, ומתי לבדוק טיפוס ומתי לתפוס את השגיאה.
TypeError פירושו שהשתמשתם בערך בצורה שהטיפוס שלו לא תומך בה. התיעוד מתאר אותה כ"פעולה או פונקציה שהופעלה על אובייקט מטיפוס לא מתאים". זו אחת השגיאות הנפוצות ביותר.
כל הדוגמאות הורצו עם Python 3.13 ונבדקו מול התיעוד של TypeError והתיעוד של isinstance().
הודעות TypeError נפוצות
קוראים את השורה האחרונה של השגיאה. היא מציינת את הטיפוסים המעורבים.
ערבוב טקסט ומספרים, לרוב ערך מ-input(), שתמיד מחזירה str:
"5" + 1
TypeError: can only concatenate str (not "int") to str
פונקציה שלא מקבלת את הטיפוס:
len(5)
TypeError: object of type 'int' has no len()
שימוש ב-None כערך. זה קורה לרוב כשפונקציה שכחה return:
None + 1
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
קריאה למשהו שאינו פונקציה:
x = 5
x()
TypeError: 'int' object is not callable
גישה באינדקס למשהו בלי פריטים:
x = 5
x[0]
TypeError: 'int' object is not subscriptable
לולאה על מספר:
for item in 5:
pass
TypeError: 'int' object is not iterable
מספר ארגומנטים שגוי:
len()
TypeError: len() takes exactly one argument (0 given)
השוואה בין טיפוסים שאי אפשר לסדר:
3 < "4"
TypeError: '<' not supported between instances of 'int' and 'str'
הניסוח המדויק משתנה בין גרסאות של Python. סוג השגיאה והרעיון לא משתנים.
TypeError או ValueError?
התיעוד מבחין בין השתיים. העברת טיפוס שגוי, כמו רשימה במקום int, צריכה לתת TypeError. העברת טיפוס נכון עם ערך לא תקין, כמו int("abc"), נותנת ValueError.
בדיקת טיפוס: type() ו-isinstance()
type() מחזירה את הטיפוס המדויק של ערך. isinstance() שואלת אם ערך הוא מטיפוס מסוים, או מתת-מחלקה שלו:
print(type(5), type(5) == int, type(True) == int)
print(isinstance(5, int), isinstance(True, int), isinstance(5.0, int))
<class 'int'> True False
True True False
type(True) == int הוא False, אבל isinstance(True, int) הוא True, כי bool היא תת-מחלקה של int. מסיבה זו התיעוד ממליץ על isinstance() לבדיקת טיפוסים.
isinstance מקבלת tuple, שפירושו “אחד מאלה”:
print(isinstance(5, (int, float)), isinstance("5", (int, float)))
True False
בדיקה לפני שימוש בערך
פונקציה יכולה לבדוק את הקלט שלה ולהעלות שגיאה ברורה:
def safe_add(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("numbers only")
return a + b
print(safe_add(1, 2.5))
safe_add(1, "2")
3.5
TypeError: numbers only
לא להגזים. Python מוגדרת עם טיפוסים דינמיים, ופונקציה שעובדת עם כל ערך שיש לו את ההתנהגות הנכונה לרוב שימושית יותר מפונקציה שמתעקשת על טיפוס אחד.
לתפוס במקום לבדוק
לפעמים פשוט יותר לנסות ולטפל בכישלון:
def to_number(text):
try:
return int(text)
except ValueError:
return float(text)
print(to_number("3"), to_number("3.5"))
3 3.5
רמזי טיפוסים לא נאכפים
אפשר להוסיף לפונקציה רמזי טיפוסים (type hints):
def double(x: int) -> int:
return x * 2
print(double(4))
print(double("ab"))
8
abab
התיעוד אומר: “סביבת הריצה של Python לא אוכפת הערות טיפוס של פונקציות ומשתנים. אפשר להשתמש בהן בכלים של צד שלישי כמו בודקי טיפוסים, סביבות פיתוח, linters ועוד”. בודק כמו mypy היה מסמן את הקריאה השנייה לפני ההרצה. בזמן ריצה הרמזים רק נשמרים:
print(double.__annotations__)
{'x': <class 'int'>, 'return': <class 'int'>}
מה הלאה
הצעד הבא בסדרה: חריגות, הסיפור המלא של try, except, else ו-finally.