רשימות

הגדרה

רשימה, כשמה כן היא, מייצגת אוסף מסודר של ערכים. רשימות יהיו סוג הנתונים הראשון שנכיר בפייתון, שמטרתו היא לקבץ ערכים.
הרעיון מוכר לנו מהיום־יום: רשימת פריטים לקנייה בסופר שמסודרת לפי הא–ב, או רשימת ההופעות בקיץ הקרוב המסודרת לפי תאריך.

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

0 1 2 3 4 5
"David Ben-Gurion" "Moshe Sharett" "David Ben-Gurion" "Levi Eshkol" "Yigal Alon" "Golda Meir"
-6 -5 -4 -3 -2 -1


דוגמה לרשימה: 6 ראשי הממשלה הראשונים בישראל לפי סדר כהונתם, משמאל לימין

דוגמאות

  1. רשימת שמות ראשי הממשלה במדינת ישראל לפי סדר כהונתם.
  2. רשימת הגילים של התלמידים בכיתה, מהמבוגר לצעיר.
  3. רשימת שמות של התקליטים שיש לי בארון, מסודרת מהתקליט השמאלי לימני.
  4. רשימה שבה כל איבר מייצג אם לראש הממשלה שנמצא בתא התואם ברשימה הקודמת היו משקפיים.
  5. האיברים 42, 8675309, 73, -40 ו־186282 בסדר הזה.
  6. רשימה של תחזית מזג האוויר ב־7 הימים הקרובים. כל איבר ברשימה הוא בפני עצמו רשימה, שמכילה שני איברים: הראשון הוא מה תהיה הטמפרטורה הממוצעת, והשני הוא מה תהיה הלחות הממוצעת.

תרגול: הרשימות שהוצגו למעלה הן רשימות הומוגניות, כאלו שכל האיברים שבהן הם מאותו סוג.
כתבו עבור כל אחת מהרשימות שהוצגו בדוגמה מה סוג הנתונים שיישמר בהן.

תרגול: נסו לתת דוגמה לעוד 3 רשימות שבהן נתקלתם לאחרונה.

רשימות בקוד

רשימות הן אחד מסוגי הנתונים הכיפיים ביותר בפייתון, וזאת בזכות הגמישות האדירה שיש לנו בתכנות עם רשימות.

הגדרת רשימה

נגדיר בעזרת פייתון את הרשימה שפגשנו למעלה – 6 ראשי הממשלה הראשונים מאז קום המדינה:


In [ ]:
prime_ministers = ['David Ben-Gurion', 'Moshe Sharett', 'David Ben-Gurion', 'Levi Eshkol', 'Yigal Alon', 'Golda Meir']

מה התרחש בקוד?
התחלנו את הגדרת הרשימה באמצעות התו [.
מייד אחרי התו הזה הכנסנו איברים לרשימה לפי הסדר הרצוי, כאשר כל איבר מופרד ממשנהו בפסיק (,).
במקרה שלנו, כל איבר הוא מחרוזת המייצגת ראש ממשלה. הכנסנו את ראשי הממשלה לרשימה לפי סדר כהונתם.
שימו לב שהרשימה מכילה איבר מסוים פעמיים – מכאן שרשימה היא מבנה נתונים שתומך בחזרות.
לסיום, נסגור את הגדרת הרשימה באמצעות התו ].


In [ ]:
print(prime_ministers)

In [ ]:
type(prime_ministers)

נוכל להגדיר רשימה של המספרים הטבעיים עד 7:


In [ ]:
numbers = [1, 2, 3, 4, 5, 6, 7]

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

לשם הדוגמה, נגדיר רשימה הטרוגנית:


In [ ]:
wtf = ['The cake is a', False, 42]

נוכל אפילו להגדיר רשימה ריקה, שבה אין איברים כלל:


In [ ]:
empty_list = []

גישה לאיברי הרשימה

לכל תא ברשימה יש מספר, שמאפשר לנו להתייחס לאיבר שנמצא באותו תא.
הדבר דומה ללייזר שעליו יש מדבקת שם ("שמות ראשי ממשלה"), והוא מצביע על שורת לייזרים שעל התווית שלהם מופיע מספר המתאר את מיקומם בשורה.
התא השמאלי ביותר ברשימה ממוספר כ־0, התא שנמצא אחריו (מימינו) מקבל את המספר 1, וכך הלאה עד לסוף הרשימה.
המספור של כל תא נקרא המיקום שלו ברשימה, או האינדקס שלו.

נגדיר את רשימת שמות התקליטים שיש לי בבית:


In [ ]:
# Index        0               1             2                     3                      4                   5
vinyls = ['Ecliptica', 'GoT Season 6', 'Lone Digger', 'Everything goes numb', 'Awesome Mix Vol. 1', 'Ultimate Sinatra']

בהנחה שאנחנו מתים על Guardians of the Galaxy, נוכל לנסות להשיג מהרשימה את Awesome Mix Vol. 1.
כדי לעשות זאת, נציין את שם הרשימה שממנה אנחנו רוצים לקבל את האיבר, ומייד לאחר מכן את מיקומו ברשימה בסוגריים מרובעים.


In [ ]:
print(vinyls[4])

התא הראשון ממוספר 0, ולא 1.
יש לכך סיבות טובות, אבל פעמים רבות תרגישו שהמספור הזה לא טבעי ועלול ליצור באגים, שהם קטעי קוד שמתנהגים אחרת משציפה המתכנת.
כפועל יוצא, המיקום ברשימה של התא האחרון לא יהיה כאורך הרשימה, אלא כאורך הרשימה פחות אחד.
משמע: ברשימה שבה 3 איברים, מספרו של התא האחרון יהיה 2.

רשימת (חלק מ)התקליטים בארון שלי, מסודרת מהתקליט השמאלי לימני.

כפי שניתן לראות בתמונה, פייתון מנסה לעזור לנו ומאפשרת לנו לגשת לאיברים גם מהסוף.
חוץ מהמספור הרגיל שראינו קודם, אפשר לגשת לאיברים מימין לשמאל באמצעות מספור שלילי.
האיבר האחרון יקבל את המספר -1, זה שלפניו (משמאלו) יקבל -2 וכן הלאה.


In [ ]:
#             0               1             2                     3                      4                   5
vinyls = ['Ecliptica', 'GoT Season 6', 'Lone Digger', 'Everything goes number', 'Awesome Mix Vol. 1', 'Ultimate Sinatra']
#            -6             -5             -4                  -3                       -2                  -1

אם נרצה לגשת שוב לאותו דיסק, אבל הפעם מהסוף, נוכל לכתוב זאת כך:


In [ ]:
print(vinyls[-2])

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


In [ ]:
type(vinyls[0])

In [ ]:
print(vinyls[0] + ', By Sonata Arctica')

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


In [ ]:
# כמה תקליטים יש לי?
len(vinyls)

אם ננסה לגשת לתא שאינו קיים, נקבל IndexError.
זה בדרך כלל קורה כשאנחנו שוכחים להתחיל לספור מ־0.
אם השגיאה הזו מופיעה כשאתם מתעסקים עם רשימות, חשבו איפה בקוד פניתם לתא שאינו קיים.

השמה ברשימות

לפעמים נרצה לשנות את הערך של האיברים ברשימה.
נפנה ללייזר מסוים בשורת הלייזרים שלנו, ונבקש ממנו להצביע לערך חדש:


In [ ]:
print(vinyls)
vinyls[1] = 'GoT Season 7'
print(vinyls)

אופרטורים חשבוניים על רשימות

אופרטורים שהכרנו כשלמדנו על מחרוזות, יעבדו נהדר גם על רשימות.

כפי ש־+ משרשר בין מחרוזות, הוא יודע לשרשר גם בין רשימות:


In [ ]:
[1, 2, 3] + [4, 5, 6]

In [ ]:
['a', 'b', 'c'] + ['easy', 'as'] + [1, 2, 3]

וכפי ש־* משרשר מחרוזת לעצמה כמות מסוימת של פעמים, כך הוא יפעל גם עם רשימות:


In [ ]:
['wake up', 'go to school', 'sleep'] * 365

אפשר גם לשלב:


In [ ]:
['Is', 'someone', 'getting'] + ['the', 'best,'] * 4 + ['of', 'you?']

שימו לב שכל אופרטור שתשימו ליד הרשימה מתייחס לרשימה בלבד, ולא לאיברים שבתוכה.
משמע + 5 לא יוסיף לכם 5 לכל אחד מהאיברים, אלא ייכשל כיוון שפייתון לא יודעת לחבר רשימה למספר שלם.


In [ ]:
[1, 2, 3] + 5

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


In [ ]:
prime_ministers = ['David Ben-Gurion', 'Moshe Sharett', 'David Ben-Gurion', 'Levi Eshkol', 'Yigal Alon', 'Golda Meir']

In [ ]:
print(prime_ministers)
prime_ministers + ['Yitzhak Rabin']
print(prime_ministers)

In [ ]:
print(prime_ministers)
prime_ministers = prime_ministers + ['Yitzhak Rabin']
print(prime_ministers)

אופרטורים השוואתיים על רשימות

נגדיר את רשימת האנשים שנכחו בכיתה ביום ראשון, שני, שלישי ורביעי:


In [ ]:
pupils_in_sunday = ['Moshe', 'Dukasit', 'Michelangelo']
pupils_in_monday = ['Moshe', 'Dukasit', 'Master Splinter']
pupils_in_tuesday = ['Moshe', 'Dukasit', 'Michelangelo']
pupils_in_wednesday = ['Moshe', 'Dukasit', 'Michelangelo', 'Master Splinter']

רשימות תומכות בכל אופרטורי ההשוואה שלמדנו עד כה.
נתחיל בקל ביותר. בואו נבדוק באיזה יום הרכב התלמידים בכיתה היה זהה להרכב התלמידים שהיה בה ביום ראשון:


In [ ]:
print("Is it Monday? " + str(pupils_in_sunday == pupils_in_monday))
print("Is it Tuesday? " + str(pupils_in_sunday == pupils_in_tuesday))
print("Is it Wednesday? " + str(pupils_in_sunday == pupils_in_wednesday))

האם משה נכח בכיתה ביום שלישי?


In [ ]:
print('Moshe' in pupils_in_tuesday)
# זה אותו דבר כמו:
print('Moshe' in ['Moshe', 'Dukasit', 'Michelangelo'])

נוכיח שמאסטר ספלינטר הבריז באותו יום:


In [ ]:
'Master Splinter' not in pupils_in_tuesday

ולסיום, בואו נבדוק איזו גרסה חדשה יותר:


In [ ]:
python_new_version = [3, 7, 2]
python_old_version = [2, 7, 16]
print(python_new_version > python_old_version)

כדי לבצע השוואה בין רשימות, פייתון מנסה להשוות את האיבר הראשון מהרשימה הראשונה לאיבר הראשון מהרשימה השנייה.
אם יש "תיקו", היא תעבור לאיבר השני בכל רשימה, כך עד סוף הרשימה.

רשימה של רשימות

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


In [ ]:
pupils_in_sunday = ['Moshe', 'Dukasit', 'Michelangelo']
pupils_in_monday = ['Moshe', 'Dukasit', 'Splinter']
pupils_in_tuesday = ['Moshe', 'Dukasit', 'Michelangelo']
pupils_in_wednesday = ['Moshe', 'Dukasit', 'Michelangelo', 'Splinter']

אנחנו רואים לפנינו רשימה של ימים, שקל להכניס לרשימה אחת גדולה:


In [ ]:
pupils = [pupils_in_sunday, pupils_in_monday, pupils_in_tuesday, pupils_in_wednesday]
print(pupils)
0 1 2 3
0 1 2
"Moshe" "Dukasit" "Michelangelo"
-3 -2 -1
0 1 2
"Moshe" "Dukasit" "Splinter"
-3 -2 -1
0 1 2
"Moshe" "Dukasit" "Michelangelo"
-3 -2 -1
0 1 2 3
"Moshe" "Dukasit" "Michelangelo" "Splinter"
-4 -3 -2 -1
-4 -3 -2 -1


דוגמה לרשימה של רשימות: נוכחות התלמידים בימי ראשון עד רביעי

השורה שכתבנו למעלה זהה לחלוטין לשורה הבאה, שבה אנחנו מגדירים רשימה אחת שכוללת את רשימות התלמידים שנכחו בכיתה בכל יום.


In [ ]:
pupils = [['Moshe', 'Dukasit', 'Michelangelo'], ['Moshe', 'Dukasit', 'Splinter'], ['Moshe', 'Dukasit', 'Michelangelo'], ['Moshe', 'Dukasit', 'Michelangelo', 'Splinter']]

נוכל לקבל את רשימת התלמידים שנכחו ביום ראשון בצורה הבאה:


In [ ]:
pupils[0]

ואת התלמיד האחרון שנכח ביום ראשון בצורה הבאה:


In [ ]:
pupils_in_sunday = pupils[0]
print(pupils_in_sunday[-1])
# או פשוט:
print(pupils[0][-1])

אם קשה לכם לדמיין את זה, עשו זאת בשלבים.
בדקו מה יש ב־pupils, אחרי זה מה מחזיר pupils[0], ואז נסו לקחת ממנו את האיבר האחרון, pupils[0][-1]. </p

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


In [ ]:
print("pupils = " + str(pupils))
print("-" * 50)
print("1. 'Moshe' in pupils == "                              + str('Moshe' in pupils))
print("2. 'Moshe' in pupils[0] == "                           + str('Moshe' in pupils[0]))
print("3. ['Moshe', 'Splinter'] in pupils == "                + str(['Moshe', 'Splinter'] in pupils))
print("4. ['Moshe', 'Splinter'] in pupils[0] == "             + str(['Moshe', 'Splinter'] in pupils[-1]))
print("5. ['Moshe', 'Dukasit', 'Splinter'] in pupils == "     + str(['Moshe', 'Dukasit', 'Splinter'] in pupils))
print("6. ['Moshe', 'Dukasit', 'Splinter'] in pupils[0] == "  + str(['Moshe', 'Dukasit', 'Splinter'] in pupils[0]))
  1. הביטוי הבוליאני בשורה 1 מחזיר False, כיוון שכל אחד מהאיברים ברשימה pupils הוא רשימה, ואף אחד מהם אינו המחרוזת "Moshe".
  2. הביטוי הבוליאני בשורה 2 מחזיר True, כיוון שהאיבר הראשון ב־pupils הוא רשימה שמכילה את המחרוזת "Moshe".
  3. הביטוי הבוליאני בשורה 3 מחזיר False, כיוון שאין בתוך pupils רשימה שאלו בדיוק הערכים שלה. יש אומנם רשימה שמכילה את האיברים האלו, אבל השאלה הייתה האם הרשימה הגדולה (pupils) מכילה איבר ששווה בדיוק ל־['Moshe', 'Splinter'].
  4. הביטוי הבוליאני בשורה 4 מחזיר False, כיוון שברשימה האחרונה בתוך pupils אין איבר שהוא הרשימה ["Moshe", "Splinter"].
  5. הביטוי הבוליאני בשורה 5 מחזיר True, כיוון שיש רשימה ישירות בתוך pupils שאלו הם ערכיה.
  6. הביטוי הבוליאני בשורה 6 מחזיר False, כיוון שברשימה הראשונה בתוך pupils אין איבר שהוא הרשימה הזו.

זכרו שעבור פייתון אין שום דבר מיוחד ברשימה של רשימות. היא בסך הכול רשימה רגילה, שכל אחד מאיבריה הוא רשימה.
מבחינתה אין הבדל בין רשימה כזו לכל רשימה אחרת.

המונח Iterable

באתרי אינטרנט ובתיעוד של פייתון אנחנו נפגש פעמים רבות עם המילה Iterable.
בקורס נשתמש במונח הזה פעמים רבות כדי להבין טוב יותר איך פייתון מתנהגת.
נגדיר ערך כ־iterable אם ניתן לפרק אותו לכלל האיברים שלו.

עד כה אנחנו מכירים 2 סוגי משתנים שעונים להגדרה iterables: רשימות ומחרוזות.
ניתן לפרק רשימה לכל האיברים שמרכיבים אותה, וניתן לפרק מחרוזת לכל התווים שמרכיבים אותה.

יש הרבה במשותף לכל הדברים שניתן להגיד עליהם שהם iterables:
על חלק גדול מה־iterables אפשר להפעיל פעולות שמתייחסות לכלל האיברים שבהם, כמו len שמראה את מספר האיברים בערך.
על חלק גדול מה־iterables יהיה אפשר גם להשתמש בסוגריים מרובעים כדי לגשת לאיבר מסוים שנמצא בהם.
בעתיד נלמד על עוד דברים שמשותפים לרוב (או לכל) ה־iterables.

מונחים

רשימה
סוג משתנה שמטרתו לקבץ ערכים אחרים בסדר מסוים.
תא
מקום ברשימה שמכיל איבר כלשהו.
מיקום
מיקום של תא מסוים הוא המרחק שלו מהתא הראשון ברשימה, שמיקומו הוא 0. זהו מספר שמטרתו לאפשר גישה לתא מסוים ברשימה.
אינדקס
מילה נרדפת ל"מיקום".
איבר
ערך שנמצא בתא של רשימה. ניתן לאחזר אותו אם נציין את שם הרשימה, ואת מיקום התא שבו הוא נמצא.
רשימה הומוגנית
רשימה שבה כל האיברים הם מאותו סוג.
רשימה הטרוגנית
רשימה שבה לכל איבר יכול להיות סוג שונה.
Iterable
ערך שמורכב מסדרה של ערכים אחרים.

לסיכום

  1. מספר האיברים ברשימה יכול להיות 0 (רשימה ריקה) או יותר.
  2. לאיברים ברשימה יש סדר.
  3. כל איבר ברשימה ממוספר החל מהאיבר הראשון שממוספר 0, ועד האיבר האחרון שמספרו הוא אורך הרשימה פחות אחד.
  4. ניתן לגשת לאיבר גם לפי המיקום שלו וגם לפי המרחק שלו מסוף הרשימה, באמצעות התייחסות למיקום השלילי שלו.
  5. איברים ברשימה יכולים לחזור על עצמם.
  6. רשימה יכולה לכלול איברים מסוג אחד בלבד (רשימה הומוגנית) או מכמה סוגים שונים (רשימה הטרוגנית).
  7. אורך הרשימה יכול להשתנות במהלך ריצת התוכנית.

תרגול

סדר בבית המשפט!

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


In [ ]:
judges = ['Esther Hayut', 'Miriam Naor', 'Asher Grunis', 'Dorit Beinisch', 'Aharon Barak']

בונוס: כתבו קטע קוד שבודק שהרשימה (שמכילה 5 איברים) אכן מסודרת.

מה זה משובחה בכלל?

לפניכם רשימה של שמות טעמי גלידה שנמצאים בדוכן הגלידה השכונתי.
קבלו מהמשתמש קיפיק את הטעם האהוב עליו, והדפיסו למשתמש האם הטעם שלו נמכר בדוכן.


In [ ]:
ice_cream_flavours = ['chocolate', 'vanilla', 'pistachio', 'banana']

מה רש"י?

לפניכם כמה ביטויים.
רשמו לעצמכם מה תהיה תוצאת כל ביטוי, ורק אז הריצו אותו.


In [ ]:
rabanim = ['Rashi', 'Maimonides', 'Nachmanides', 'Rabbeinu Tam']

In [ ]:
'Rashi' in rabanim

In [ ]:
'RASHI' in rabanim

In [ ]:
['Rashi'] in rabanim

In [ ]:
['Rashi', 'Nachmanides'] in rabanim

In [ ]:
'Bruria' in rabanim

In [ ]:
rabanim + ['Gershom ben Judah']
'Gershom ben Judah' in rabanim

In [ ]:
'3' in [1, 2, 3]

In [ ]:
(1 + 5 - 3) in [1, 2, 3]

In [ ]:
[1, 5, 3] > [1, 2, 3]

In [ ]:
rabanim[0] in [rabanim[0] + rabanim[1]]

In [ ]:
rabanim[0] in [rabanim[0]] + [rabanim[1]]

In [ ]:
rabanim[-1] == rabanim[0] or rabanim[-1] == rabanim[1] or rabanim[-1] == rabanim[2] or rabanim[-1] == rabanim[3]

In [ ]:
rabanim[-1] == rabanim[0] or rabanim[-1] == rabanim[1] or rabanim[-1] == rabanim[2] and rabanim[-1] != rabanim[3]

In [ ]:
rabanim[-1] == rabanim[0] or rabanim[-1] == rabanim[1] or rabanim[-1] == rabanim[2] and rabanim[-1] == rabanim[3]

In [ ]:
1 in [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [ ]:
[1, 2, 3] in [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [ ]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][2]

In [ ]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][3]

In [ ]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][-1] * 5

In [ ]:
[[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][-1]] * 5

In [ ]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][-1]

In [ ]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]][0][-1] == [[7, 8, 9], [4, 5, 6], [1, 2, 3]][2][2]

In [ ]:
[[1, 2, 3]] in [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [ ]:
[[1, 2, 3], [4, 5, 6]] in [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

In [ ]:
[[1, 2, 3], [4, 5, 6]] in [[[1, 2, 3], [4, 5, 6]], [7, 8, 9]]