ברשימה הבאה, כל תבליט מייצג אוסף של נתונים:
נסו למצוא מאפיינים משותפים לאוספים שהופיעו מעלה.
חשוב!
פתרו לפני שתמשיכו!
אפשר לחלק כל אחד מהאוספים שכתבנו למעלה ל־2 קבוצות ערכים.
הראשונה – הנושאים של האוסף. עבור החנות של קשטן, לדוגמה, הפריט שאנחנו מחזיקים בחנות.
השנייה – הפריטים שהם נתון כלשהו בנוגע לפריט הראשון: המלאי של אותו פריט, לדוגמה.
פריטים מהקבוצה הראשונה לעולם לא יחזרו על עצמם – אין היגיון בכך ש"תפוח ירוק" יופיע פעמיים ברשימת המלאי בחנות, ולא ייתכן מצב של שני מספרי זהות זהים.
הפריטים מהקבוצה השנייה, לעומת זאת, יכולים לחזור על עצמם – הגיוני שתהיה אותה כמות של בננות ותפוחים בחנות, או שיהיו אנשים בעלי מספרי זהות שונים שנקראים "משה כהן".
נבחן לעומק את המאפיינים המשותפים בדוגמאות שלעיל.
| אוסף | הערך הקושר (קבוצה ראשונה) | הערך המתאים לו (קבוצה שנייה) | הסבר |
|---|---|---|---|
| מוצרים והמלאי שלהם בחנות | המוצר שנמכר בחנות | המלאי מאותו מוצר | יכולים להיות בחנות 5 תפוזים ו־5 תפוחים, אבל אין משמעות לחנות שיש בה 5 תפוחים וגם 3 תפוחים. |
| מספרי הזהות של אזרחים | תעודת הזהות | השם של בעל מספר הזהות | יכולים להיות הרבה אזרחים העונים לשם משה לוי, ולכל אחד מהם יהיה מספר זהות שונה. לא ייתכן שמספר זהות מסוים ישויך ליותר מאדם אחד. |
| בעלות על צוללות צבעוניות | בעל הצוללות | צבע הצוללות | יכול להיות שגם לקווין וגם לג'ני יש צוללות בצבעים זהים. ג'ני, קווין ואני הם אנשים ספציפיים, שאין יותר מ־1 מהם בעולם (עד שנמציא דרך לשבט אנשים). |
| מוצרים ומחיריהם | שם המוצר | מחיר המוצר | לכל מוצר מחיר נקוב. עבור שני מוצרים שונים בחנות יכול להיות מחיר זהה. |
כמו שראינו בדוגמאות, מצב נפוץ במיוחד הוא הצורך לאחסן מיפוי בין ערכים.
נחשוב על המיפוי בחנות של קשטן, שבה הוא סופר את המלאי עבור כל מוצר.
נוכל לייצג את מלאי המוצרים בחנות של קשטן באמצעות הידע שכבר יש לנו. נשתמש בקוד הבא:
In [ ]:
items = ['banana', 'apple', 'carrot']
stock = [2, 3, 4]
עבור כל תא ברשימת items, שמרנו במקום התואם ברשימת stock את הכמות שנמצאת ממנו בחנות.
יש 4 גזרים, 3 תפוחים ו־2 בננות על המדף בחנות של אדון קשטן.
שליפה של כמות המלאי עבור מוצר כלשהו בחנות תתבצע בצורה הבאה:
In [ ]:
def get_stock(item_name, items, stock):
item_index = items.index(item_name)
how_many = stock[item_index]
return how_many
בשורה הראשונה בגוף הפונקציה מצאנו את מיקום המוצר שאנחנו מחפשים במלאי. נניח, "תפוח" מוחזק במקום 1 ברשימה.
בשורה השנייה פנינו לרשימה השנייה, זו שמאחסנת את המלאי עבור כל מוצר, ומצאנו את המלאי שנמצא באותו מיקום.
כמות היחידות של מוצר מאוחסנת במספר תא מסוים, התואם למספר התא ברשימה של שמות המוצרים. זו הסיבה לכך שהרעיון עובד.
In [ ]:
print(get_stock('apple', items, stock))
צורה נוספת למימוש אותו רעיון תהיה שמירה של זוגות סדורים בתוך רשימה של tuple־ים:
In [ ]:
items = [('banana', 2), ('apple', 3), ('carrot', 4)]
ברשימה הזו הרעיון נראה מובן יותר. בואו נממש דרך לחלץ איבר מסוים מתוך הרשימה:
In [ ]:
def get_stock(item_name_to_find, items_with_stock):
for item_to_stock in items_with_stock:
item_name = item_to_stock[0]
stock = item_to_stock[1]
if item_name == item_name_to_find:
return stock
עבור כל tuple ברשימה, בדקנו אם שם הפריט שהוא מכיל תואם לשם הפריט שחיפשנו.
אם כן, החזרנו את הכמות של אותו פריט במלאי.
שימוש בפונקציה הזו נראה כך:
In [ ]:
get_stock('apple', items)
השתמשו ב־unpacking שלמדנו במחברת הקודמת כדי לפשט את לולאת ה־for בקוד של get_stock.
חשוב!
פתרו לפני שתמשיכו!
שני קטעי הקוד שנתנו כדוגמה פישטו את המצב יתר על המידה, והם אינם מתייחסים למצב שבו הפריט חסר במלאי.
הרחיבו את הפונקציות get_stock כך שיחזירו 0 אם הפריט חסר במלאי.
חשוב!
פתרו לפני שתמשיכו!
מילון הוא סוג ערך בפייתון.
תכליתו היא ליצור קשר בין סדרה של נתונים שנקראת מפתחות, לבין סדרה אחרת של נתונים שנקראת ערכים.
לכל מפתח יש ערך שעליו הוא מצביע.
ישנן דוגמאות אפשריות רבות לקשרים כאלו:
לערך המצביע נקרא מפתח (key). זה האיבר מבין זוג האיברים שעל פיו נשמע הגיוני יותר לעשות חיפוש:
לערך השני מבין שני הערכים בזוג, נקרא... ובכן, ערך (value). זה הנתון שנרצה למצוא לפי המפתח:
אם כך, מילון הוא בסך הכול אוסף של זוגות שכאלו: מפתחות וערכים.
ניצור מילון חדש:
In [ ]:
ages = {'Yam': 27, 'Methuselah': 969, 'Baby Groot': 3}
במילון הזה ישנם שלושה ערכים: הגיל של ים, של מתושלח ושל בייבי־גרוט.
המפתחות במילון הזה הם Yam (הערך הקשור למפתח הזה הוא 27), Methuselah (עם הערך 969) ו־Baby Groot (אליו הוצמד הערך 3).
יצרנו את המילון כך:
אפשר ליצור מילון ריק בעזרת פתיחה וסגירה של סוגריים מסולסלים:
In [ ]:
age_of_my_elephants = {}
צרו מילון עבור המלאי בחנות של אדון קשטן.
חשוב!
פתרו לפני שתמשיכו!
ניזכר כיצד מאחזרים ערך מתוך רשימה:
In [ ]:
names = ['Yam', 'Mathuselah', 'Baby Groot']
כדי לחלץ את הערך שנמצא במקום 2 ברשימה names, נכתוב:
In [ ]:
names[2]
עד כאן הכול מוכר.
ניקח את המילון שמייצג את המלאי בחנות של אדון קשטן:
In [ ]:
items = {'banana': 2, 'apple': 3, 'carrot': 4}
כדי לחלץ את ערך המלאי שנמצא במקום שבו המפתח הוא 'banana', נרשום את הביטוי הבא:
In [ ]:
items['banana']
כיוון שבמילון המפתח הוא זה שמצביע על הערך ולא להפך, אפשר לאחזר ערך לפי מפתח, אבל אי־אפשר לאחזר מפתח לפי ערך.
ביום־יום, השתמשו במילה "בְּמָקוֹם" (b'e-ma-qom) כתחליף למילים סוגריים מרובעים.
לדוגמה: עבור שורת הקוד האחרונה, אימרו items במקום banana
.
אפשר להוסיף מפתח וערך למילון, באמצעות השמת הערך אל המילון במקום של המפתח.
ניקח כדוגמה מקרה שבו יש לנו במלאי מלון אחד.
המפתח הוא melon והערך הוא 1, ולכן נשתמש בהשמה הבאה:
In [ ]:
items['melon'] = 1
אם הגיעו עוד 4 מלונים לחנות של אדון קשטן, נוכל לעדכן את מלאי המלונים באמצעות השמה למקום הנכון במילון:
In [ ]:
items['melon'] = items['melon'] + 4
בגרסאות האחרונות של פייתון הפך מילון להיות מבנה סדור, שבו סדר האיברים הוא סדר ההכנסה שלהם למילון.
למרות זאת, רק במצבים נדירים נצטרך להתייחס לסדר שבו האיברים מסודרים במילון, ובשלב זה נעדיף שלא להתייחס לתכונה הזו.
חדי העין שמו לב שאנחנו מצליחים להוסיף ערכים למילון, ולשנות בו ערכים קיימים.
מהתכונה הזו אנחנו למדים שמילון הוא mutable.
כיוון שמילון הוא iterable, דרך מקובלת לעבור עליו היא באמצעות לולאת for.
ננסה להשתמש בלולאת for על מילון, ונראה מה התוצאות:
In [ ]:
favorite_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
for something in favorite_animals:
print(something)
נראה שקיבלנו רק את המפתחות, בלי הערכים.
נסיק מכאן שמילון הוא אמנם iterable, אך בכל איטרציה הוא מחזיר לנו רק את המפתח, בלי הערך.
אנחנו כבר יודעים איך מחלצים את הערך של מפתח מסוים.
נוכל להשתמש בידע הזה כדי לקבל בכל חזרור גם את המפתח, וגם את הערך:
In [ ]:
favorite_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
print('favorite_animals items:')
for key in favorite_animals:
value = favorite_animals[key]
print(f"{key:10} -----> {value}.") # תרגיל קטן: זהו את הטריק שגורם לזה להיראות טוב כל כך בהדפסה
אבל הפתרון הזה לא נראה אלגנטי במיוחד, ונראה שנוכל למצוא אחד טוב יותר.
לעזרתנו נחלצת הפעולה items, השייכת לערכים מסוג מילון.
הפעולה הזו מחזירה זוגות איברים, כאשר בכל זוג האיבר הראשון הוא המפתח והאיבר השני הוא הערך.
In [ ]:
print(list(favorite_animals.items()))
מאחר שמדובר באיברים שבאים בזוגות, נוכל להשתמש בפירוק איברים כפי שלמדנו בשיעור על לולאות for:
In [ ]:
print('favorite_animals items:')
for key, value in favorite_animals.items():
print(f"{key:10} -----> {value}.")
בלולאה שמופיעה למעלה ניצלנו את העובדה שהפעולה items מחזירה לנו איברים בזוגות: מפתח וערך.
בכל חזרור, אנחנו מכניסים למשתנה key את האיבר הראשון בזוג, ולמשתנה value את האיבר השני בזוג.
נוכל להיות אפילו אלגנטיים יותר ולתת למשתנים הללו שמות ראויים:
In [ ]:
print('favorite_animals items:')
for character, animal in favorite_animals.items():
print(f"{character:10} -----> {animal}.")
כתבו פונקציה שמקבלת מילון ומדפיסה עבור כל מפתח את האורך של הערך המוצמד אליו.
מילונים הם טיפוסים קצת רגישים. הם לא אוהבים כשמזכירים להם מה אין בהם.
אם ננסה לפנות למילון ולבקש ממנו מפתח שאין לו, נקבל הודעת שגיאה.
בפעמים הראשונות שתתעסקו עם מילונים, יש סיכוי לא מבוטל שתקבלו KeyError שנראה כך:
In [ ]:
empty_dict = {}
empty_dict['DannyDin']
יש כמה דרכים לפתור בעיה זו.
דרך אפשרית אחת היא לבדוק שהמפתח קיים לפני שאנחנו ניגשים אליו:
In [ ]:
loved_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
print('Achiles' in loved_animals)
כאן השתמשנו באופרטור in כדי לבדוק אם מפתח מסוים נמצא במילון.
נוכל גם לבקש את הערך לאחר שבדקנו שהוא קיים:
In [ ]:
loved_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
if 'Achiles' in loved_animals:
value = loved_animals['Achiles']
else:
value = 'Pony'
print(value)
בקוד שלמעלה, השתמשנו באופרטור ההשוואה in כדי לבדוק אם מפתח מסוים ("אכילס") קיים בתוך המילון שיצרנו בשורה הראשונה.
אם הוא נמצא שם, חילצנו את הערך שמוצמד לאותו מפתח (ל"אכילס"). אם לא, המצאנו ערך משלנו – "פוני".
מעבר על מילון יחזיר בכל חזרור מפתח מהמילון, ללא הערך הקשור אליו.
מסיבה זו, אופרטור ההשוואה in יבדוק רק אם קיים מפתח מסוים במילון, ולא יבדוק אם ערך שכזה קיים.
כתבו פונקציה שמקבלת שלושה פרמטרים: מילון, מפתח וערך ברירת מחדל.
הפונקציה תחפש את המפתח במילון, ואם הוא קיים תחזיר את הערך שלו.
אם המפתח לא קיים במילון, הפונקציה תחזיר את ערך ברירת המחדל.
חשוב!
פתרו לפני שתמשיכו!
ננסה לכתוב את הרעיון בקוד שלמעלה כפונקציה כללית:
In [ ]:
def get_value(dictionary, key, default_value):
if key in dictionary:
return dictionary[key]
else:
return default_value
הפונקציה שלמעלה מקבלת מילון, מפתח וערך ברירת מחדל.
אם היא מוצאת את המפתח במילון, היא מחזירה את הערך של אותו מפתח.
אם היא לא מוצאת את המפתח במילון, היא מחזירה את ערך ברירת המחדל שנקבע.
נבדוק שהפונקציה עובדת:
In [ ]:
loved_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
print("Mad hatter: " + get_value(loved_animals, 'Mad hatter', 'Pony'))
print("Queen of hearts: " + get_value(loved_animals, 'Queen of hearts', 'Pony'))
ובכן, זו פונקציה כייפית. כמה נוח היה לו היא הייתה פעולה של מילון.
מי היה מאמין, יש פעולה כזו במילונים! ננסה להפעיל אותה על המילון שלנו.
שימו לב לצורת הקריאה לפעולה, ששונה מהקריאה לפונקציה שכתבנו למעלה – שם המשתנה של המילון בא לפני שם הפעולה. מדובר בפעולה, ולא בפונקציה:
In [ ]:
loved_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
print("Mad hatter: " + loved_animals.get('Mad hatter', 'Pony'))
print("Queen of hearts: " + loved_animals.get('Queen of hearts', 'Pony'))
טריק קסום אחרון שנראה הוא שהפעולה get סלחנית ממש, ומתפקדת גם אם לא נותנים לה ערך ברירת מחדל.
אם תספקו רק את שם המפתח שממנו תרצו לאחזר ערך, היא תחפש אותו ותחזיר את הערך שלו, אם הוא קיים.
אם המפתח לא קיים ולא סופק ערך ברירת מחדל, היא תחזיר את הערך None:
In [ ]:
loved_animals = {'Alice': 'Cat', 'Mad hatter': 'Hare', 'Achiles': 'Tortoise'}
print(loved_animals.get('Mad hatter'))
print(loved_animals.get('Queen of hearts'))
הערך המיוחד None הוא דרך פייתונית להגיד "כלום".
אפשר לדמיין אותו כמו רִיק (וָקוּם). לא הערך המספרי אפס, לא False. פשוט כלום.
יוגב נבו קיבל מסר מוצפן מאדון יום טוב, והצליח לשים את ידו על שיטה לפענוח המסר.
כדי לפענח את המסר, החליפו כל אות במסר הסודי באות התואמת לה, לפי המילון המופיע למטה.
לדוגמה, דאגו שכל המופעים של האות O במסר SONG יוחלפו באות A.
In [ ]:
decryption_key = {
'O': 'A', 'D': 'B', 'F': 'C', 'I': 'D', 'H': 'E',
'G': 'F', 'L': 'G', 'C': 'H', 'K': 'I', 'Q': 'J',
'B': 'K', 'J': 'L', 'Z': 'M', 'V': 'N', 'S': 'O',
'R': 'P', 'M': 'Q', 'X': 'R', 'E': 'S', 'P': 'T',
'A': 'U', 'Y': 'V', 'W': 'W', 'T': 'X', 'U': 'Y',
'N': 'Z',
}
SONG = """
sc, kg pchxh'e svh pckvl k covl svps
pcop lhpe zh pcxsalc pch vklcp
k okv'p lsvvo is wcop k isv'p wovp ps
k'z lsvvo jkyh zu jkgh
eckvkvl jkbh o ikozsvi, xsjjkvl wkpc pch ikfh
epovikvl sv pch jhilh, k ecsw pch wkvi csw ps gju
wchv pch wsxji lhpe kv zu gofh
k eou, coyh o vkfh iou
coyh o vkfh iou
"""
חברו של יום טוב, חיים, שלח ליום טוב מסר מוצפן.
למרבה הצער יוגב שם את ידיו רק על מפת ההצפנה, ולא על מפת הפענוח.
צרו ממילון ההצפנה מילון פענוח, שבו:
לדוגמה, המילון {'a': '1', 'b': 2} יהפוך למילון {'1': 'a', '2': 'b'}.
השתמשו במילון הפענוח שיצרתם כדי לפענח את המסר שנשלח.
In [3]:
encryption_key = {
'T': '1', 'F': '6', 'W': 'c', 'Y': 'h', 'B': 'k',
'P': '~', 'H': 'q', 'S': 's', 'E': 'w', 'Q': '@',
'U': '$', 'M': 'i', 'I': 'l', 'N': 'o', 'J': 'y',
'Z': 'z', 'G': '!', 'L': '#', 'A': '&', 'O': '+',
'D': ',', 'R': '-', 'C': ':', 'V': '?', 'X': '^',
'K': '|',
}
SONG = """
l1's ih #l6w
l1's o+c +- ow?w-
l &lo'1 !+oo& #l?w 6+-w?w-
l y$s1 c&o1 1+ #l?w cql#w l'i &#l?w
(l1's ih #l6w)
ih qw&-1 ls #l|w &o +~wo ql!qc&h
#l|w 6-&o|lw s&l,
l ,l, l1 ih c&h
l y$s1 c&o1 1+ #l?w cql#w l'i &#l?w
l1's ih #l6w
"""
בספרו המפורסם של טולסטוי "מלחמה ושלום" העלילה מתרחשת בתקופת הפלישה של נפוליאון לרוסיה.
אלכנדרוס הראשן התערב עם ברונו הפיל שהוא המציא פטנט, שבעזרתו הוא יכול למצוא את המספר שהופיע הכי הרבה פעמים בכל ספר שהוא.
השתמשו בקובץ המצורף war-and-peace.txt שנמצא בתיקיית resources, ועזרו לברונו הפיל למצוא את השנה שמוזכרת הכי הרבה פעמים בספר.