קבוצה (או set) בפייתון היא אוסף של איברים שמתאפיינת בתכונות הבאות:
לדוגמה:
עד מחברת זו, לו היינו מתבקשים להגדיר מבנה נתונים שיאחסן עבורנו שמות של אומנים איטלקים ידועים, היינו משתמשים ברשימה או ב־tuple־ים:
In [ ]:
# List:
italian_artists = ['Donatelo', 'Giotto', 'Leonardo', 'Masaccio', 'Michelangelo', 'Raphael', 'Titian']
# Tuple:
italian_artists = ('Donatelo', 'Giotto', 'Leonardo', 'Masaccio', 'Michelangelo', 'Raphael', 'Titian')
אך כיוון שמדובר באוסף איברים ללא חזרות וללא סדר מוגדר, אפשר לשמור אותם כ־set:
In [ ]:
italian_artists = {'Donatelo', 'Giotto', 'Leonardo', 'Masaccio', 'Michelangelo', 'Raphael', 'Titian'}
אפשר לראות שההגדרות דומות מאוד במראה שלהן.
כדי להגדיר קבוצה, נפתח סוגריים מסולסלים (כמו במילון), ונציין את איבריה, כשהם מופרדים זה מזה בפסיקים.
אם תרצו, תוכלו לדמיין קבוצה כמילון שבו יש רק מפתחות.
נוכל גם להגדיר קבוצה ריקה באמצעות הפונקציה set
.
נגדיר, לדוגמה, את קבוצת כל החדי־קרן הוורודות הבלתי נראות:
In [ ]:
invisible_pink_unicorns = set()
print(invisible_pink_unicorns)
תעלול נחמד נוסף הוא המרת iterable לקבוצה, באמצעות אותה הפונקציה:
In [ ]:
numbers = range(10)
numbers = set(numbers)
print(numbers)
בהגדרה שבתחילת הפרק ציינו שקבוצה לא תכיל כמה איברים בעלי ערך זהה.
למרות ההגבלה הזו, בהגדרת הקבוצה אפשר לספק ערכים שחוזרים על עצמם.
הקבוצה "תבלע" את הכפילויות בערכים, ותשאיר רק איבר אחד מכל ערך:
In [ ]:
print({1, 1, 2, 3, 5})
print(set('abccccccccccc'))
הגדירו קבוצה שאיבריה הם האותיות הקטנות והגדולות באנגלית.
ישנו פתרון פשוט שלא מצריך מכם לכתוב את כל הא"ב.
חשוב!
פתרו לפני שתמשיכו!
נגדיר שוב את קבוצת האומנים האיטלקים:
In [ ]:
italian_artists = {'Donatelo', 'Giotto', 'Leonardo', 'Masaccio', 'Michelangelo', 'Raphael', 'Titian'}
אחד מיתרונותיהן רבי־הערך של קבוצות הוא היכולת לבצע ביניהן פעולות בקלות.
נדגים את הרעיון בעזרת הגדרת קבוצה נוספת, הפעם של דמויות מחוברות הקומיקס של צבי הנינג'ה:
In [ ]:
ninja_turtles_characters = {'Donatelo', 'Leonardo', 'Michelangelo', 'Raphael', 'Splinter', 'Shredder'}
כעת נוכל, לדוגמה, לבחור את כל הדמויות מצבי הנינג'ה שנושאות שם של אומן איטלקי מפורסם.
הקבוצה שנוצרת מהאיברים המשותפים לשתי הקבוצות נקראת קבוצת החיתוך.
In [ ]:
ninja_turtles_characters.intersection(italian_artists)
ישנן פעולות רבות נוספות שאפשר לבצע בין קבוצות.
דרך קלה להמחיש את היחסים ההדדיים בין הקבוצות הללו היא תרשים שנקרא "דיאגרמת ון":
נוכל גם לבקש, לדוגמה, את שמות האומנים האיטלקים שאינם דמויות מצבי הנינג'ה.
נקבל בחזרה את השמות שמופיעים בתרשים בעיגול הטורקיז, אך לא בחיתוך בין שני העיגולים:
In [ ]:
italian_artists.difference(ninja_turtles_characters)
באותה צורה נוכל לבקש את שמות כל הדמויות מצבי הנינג'ה שאינם אומנים איטלקים.
אלו השמות שמופיעים אך ורק בעיגול הורוד, ולא בחיתוך בין שני העיגולים:
In [ ]:
ninja_turtles_characters.difference(italian_artists)
אפשר לבקש גם את קבוצת האיחוד, שמורכבת מכל הדמויות שקיימות בשתי הקבוצות.
אלו כל הדמויות שמופיעות בשרטוט:
In [ ]:
ninja_turtles_characters.union(italian_artists)
או את כל הדמויות שלא משותפות לשתי הקבוצות.
אלו כל הדמויות שמופיעות מחוץ לחיתוך של שני העיגולים:
In [ ]:
ninja_turtles_characters.symmetric_difference(italian_artists)
צרו קבוצה שאיבריה הם 10 מספרי פיבונאצ'י הראשונים, וקבוצה נוספת שאיבריה הם המספרים הזוגיים בין 1 ל־15.
שרטטו דיאגרמת ון של שתי הקבוצות, ובדקו שהשרטוט נאמן למציאות בעזרת הפעולות שלמדתם למעלה.
בתיקיית resources נמצאים הקבצים hamlet.txt ו־the-monkeys-paw.txt.
כמה מילים משותפות לשני החיבורים? כמה שונות?
כמה מהמילים מופיעות רק בכפת הקוף, וכמה רק בהמלט?
קבוצה היא mutable – אפשר לערוך אותה מבלי ליצור קבוצה חדשה, בדומה לרשימות או למילונים.
תכונה זו מאפשרת לנו לצרף לקבוצה איברים חדשים או להסיר ממנה איברים קיימים בקלות רבה.
נוסיף לקבוצת שמות הדמויות בצבי הנינג'ה את אפריל או'ניל באמצעות הפעולה add
:
In [ ]:
ninja_turtles_characters.add("April O'Neil")
print(ninja_turtles_characters)
ונמחק מהקבוצה את שרדר הרשע באמצעות הפעולה remove
:
In [ ]:
ninja_turtles_characters.remove("Shredder")
print(ninja_turtles_characters)
שימו לב שניסיון למחוק ערך שלא קיים בקבוצה יכעיס את פייתון:
In [ ]:
ninja_turtles_characters.remove("Krang")
אלא אם כן נשתמש בפעולה discard
, שמאפשרת לנו למחוק איברים מקבוצות, בלי לחשוש משגיאה:
In [ ]:
ninja_turtles_characters.discard("Krang")
print(ninja_turtles_characters)
ולסיום – הדמויות הרעות תמיד חוזרות לסיבוב שני.
נרחיב את הקבוצה שלנו ונוסיף אליה את הנבלים מ"שבט הרגל" (בחיי, ככה הם תרגמו את Foot Clan!) במכה אחת, באמצעות הפעולה update
:
In [ ]:
foot_clan_members = {'Shredder', 'Krang', 'Karai'}
ninja_turtles_characters.update(foot_clan_members)
print(ninja_turtles_characters)
צרו קבוצה של 100 המספרים הראשוניים הראשונים, וצרפו אליה את כל המספרים הזוגיים בין 1 ל־1,000.
צרו קבוצה נוספת של המספרים בין 1 ל־1,000 שסכום ספרותיהם קטן מ־4.
כמה מספרים משותפים לשתי הקבוצות שיצרתם?
אחת התכונות המועילות ביותר של קבוצות היא שאפשר לבצע בעזרתן בדיקות שייכות במהירות רבה.
בדיוק כמו חיפוש מפתח במילון, בקבוצה אפשר למצוא ערך בתוך זמן קצר מאוד.
In [ ]:
only_turtles = {'Donatelo', 'Raphael', 'Michelangelo', 'Leonardo'}
In [ ]:
'Donatelo' in only_turtles
אפשר גם לבדוק אם כל האיברים של קבוצה אחת נמצאים בתוך קבוצה אחרת:
In [ ]:
print(italian_artists)
In [ ]:
only_turtles.issubset(italian_artists)
ולהפך – אם קבוצה מסוימת מכילה את כל איברי הקבוצה השנייה:
In [ ]:
italian_artists.issuperset(only_turtles)
בקובץ words.txt ישנה רשימת מילים ארוכה מאוד.
כמה זמן בממוצע נמשך חיפוש המילה ברשימה? ובקבוצה?
רמז: השתמשו במודול time.
לעיתים יהיה לנו נוח יותר להשתמש באופרטורים במקום בפעולות.
לדוגמה, הפעולה של חיתוך בין שמות הדמויות בצבי הנינג'ה לשמות האומנים האיטלקים שנעשתה כך:
In [ ]:
italian_artists.intersection(ninja_turtles_characters)
יכולה להיעשות גם כך:
In [ ]:
italian_artists & ninja_turtles_characters
השימוש באופרטורים בין קבוצות יכול לקצר את הכתיבה ולשוות לה מראה נקי יותר.
ריכזנו עבורכם אופרטורים שבהם משתמשים תדיר כשעובדים עם קבוצות:
שם פעולה | שם הפעולה בפייתון | אופרטור | דוגמה |
---|---|---|---|
איחוד | union | | |
{1, 3, 5} | {1, 2, 3} מחזיר {1, 2, 3, 5} |
חיתוך | intersection | & |
{1, 3, 5} & {1, 2, 3} מחזיר {1, 3} |
הפרש | difference | - |
{1, 3, 5} - {1, 2, 3} מחזיר {5} {1, 2, 3} - {1, 3, 5} מחזיר {2}
|
הפרש סימטרי | symmetric_difference | ^ |
{1, 2, 3} ^ {1, 3, 5} מחזיר {2, 5} |
בדיקת שייכות/הכלה | issubset/issuperset | <= |
{1, 2, 3} <= {1, 3, 5} מחזיר False {1, 2, 3} <= {1, 2, 3, 4, 5} מחזיר True
|
שימו לב לכך שאופרטורים בין קבוצות לרוב יוצרים קבוצה חדשה.
במקרה שאנחנו יכולים לשנות קבוצה קיימת ולנצל את היותה של קבוצה mutable, נעדיף לבחור בדרך הפעולה הזו כדי לייעל את התוכנית שלנו.
כך, לדוגמה, הוספת איברים לקבוצה בצורה הזו:
In [ ]:
primes = {2, 3, 5, 7}
primes_to_add = {11, 13, 17, 19}
primes.update(primes_to_add)
print(primes)
עדיפה על הצורה הזו:
In [ ]:
primes = {2, 3, 5, 7}
primes_to_add = {11, 13, 17, 19}
primes = primes | primes_to_add
print(primes)
כתבו פונקציה שמקבלת נתיב ל־2 תיקיות, ומחזירה שמות של קבצים שמופיעים בשתי התיקיות.
In [ ]:
import os
def get_filenames(path):
for file in os.scandir(path):
yield file.name
def common_filenames(path1, path2):
path1_files = set(get_filenames(path1))
path2_files = set(get_filenames(path2))
return path1_files & path2_files
common_filenames('images', 'resources/week5_images')
כתבו פונקציה בשם uniquify שמקבלת רשימה של איברים, ומחזירה רשימה של אותם איברים וללא כפילויות.
הניחו שאיברי כל הרשימה הם immutable.
כתבו פונקציה בשם count_specials שמקבלת מספר שלם חיובי $n$.
הפונקציה תחזיר את מספר המספרים החיוביים הנמוכים מ־$n$, שמתחלקים ב־3 או ב־7 ללא שארית.
לדוגמה, עבור $n=22$, המספרים הם: 3, 6, 7, 9, 12, 14, 15, 18 ו־21. במקרה כזה הפונקציה תחזיר 9.
ודאו שאתם משתמשים ב־set בפתרון התרגיל.
בפריסת מקלדת סטנדרטית ישנן 3 שורות של מקשי אותיות.
האותיות שנמצאות בפינת המקלדת השמאלית־עליונה מרכיבות את הצירוף qwerty.
מבין שמות כל המדינות בארצות הברית, ישנו רק שם מדינה אחד שאפשר לכתוב בעזרת שורה אחת בלבד במקלדת.
קראו את שמות כל המדינות בארצות־הברית מהקובץ resources/states.txt.
כתבו פונקציה בשם find_special_state.
הפונקציה תחזיר את שם המדינה שאפשר להרכיב בעזרת האותיות המופיעות באותה השורה במקלדת.
לדוגמה, potter או hash הן מילים שנכתבו בעזרת שורה אחת במקלדת.
turtle נכתבה בעזרת 2 שורות, ו־ninja נכתבה בעזרת 3 שורות.