• בלוג
  • ושוב mypy הציל לי את היום

ושוב mypy הציל לי את היום

26/07/2023

שימו לב לקוד הבא בלי Type Hints:

items = [{'a': 10, 'b': 20, '_id': 1},
         {'a': 12, 'b': 31, '_id': 2}]

x = 5 # type: int

def print_item(items, index):
    i = items[index]
    del(i['_id'])
    print(i)


print_item(items, 0)
print_item(items, 1)

רואים את הבאג? הקוד מדפיס את שני המילונים אבל מקלקל את הרשימה. אם נדפיס את items בסוף התוכנית נקבל:

[{'a': 10, 'b': 20}, {'a': 12, 'b': 31}]

כלומר מאפיין _id נמחק. ברור שזו לא היתה הכוונה בכתיבת הפונקציה. print_item רק צריכה להדפיס פריט בלי השדות המיוחדים שמתחילים בסימן קו תחתי, אבל על הדרך מישהו התבלבל ומחק מידע מרשימה שהתקבלה בתור קלט.

עכשיו אותו הקוד עם Type Hints:

from typing import Mapping

Items = list[Mapping[str, int]]

items: Items = [{'a': 10, 'b': 20, '_id': 1},
                {'a': 12, 'b': 31, '_id': 2}]

x = 5 # type: int

def print_item(items: Items, index: int):
    i = items[index]
    del(i['_id'])
    print(i)


print_item(items, 0)
print_item(items, 1)

print(items)

הפעם כבר בתוך PyCharm ובטח בהפעלת mypy מקבלים את השגיאה:

demo.py:12: error: "Mapping[str, int]" has no attribute "__delitem__"; maybe "__getitem__"?  [attr-defined]
Found 1 error in 1 file (checked 1 source file)

נכון, צריך לזכור להשתמש בטיפוס הנכון (Mapping במקום dict לדברים שאתם לא רוצים שישתנו), וברור שהיה נחמד אם בדיקות כאלה היו קורות בברירת מחדל, ובכל זאת גם זו תזכורת טובה לחשיבות של Type Hints.