2.10. קבוצות¶
קבוצה היא אוסף לא מסודר של פריטים ייחודיים. הוספת ערך שכבר קיים אינה משפיעה; איטרציה מניבה כל ערך פעם אחת בדיוק. קבוצות הן הכלי הנכון כאשר חברות וסילוק כפילויות חשובים, והסדר אינו חשוב.
2.10.1. יצירת קבוצה¶
השתמשו בסוגריים מסולסלים עבור קבוצה לא ריקה, או ב-set() עבור קבוצה ריקה:
colours = {"red", "green", "blue"}
empty = set()
הסוגריים נראים כמו ליטרל של dict; {} לבדו הוא מילון ריק, לא קבוצה ריקה – אחת התאונות ההיסטוריות של Python. השתמשו ב-set() למקרה הריק.
set() גם בונה קבוצה מכל איטרבל, וזו הדרך הסטנדרטית לסילוק כפילויות מרצף:
nums = [1, 2, 2, 3, 1, 4]
unique = set(nums)
print(unique)
פלט:
{1, 2, 3, 4}
סדר ההדפסה עשוי להשתנות – קבוצות אינן מבטיחות לבצע איטרציה בסדר מסוים כלשהו.
2.10.2. קבוצה לעומת מילון¶
קבוצות ומילונים שניהם מאחסנים פריטים ייחודיים בטבלת גיבוב. מה שכל פריט נושא עמו הוא ההבדל:
dictמאחסן זוגות מפתח-ערך. חיפוש מפתח מחזיר את ערכו.setמאחסן רק את הפריטים. חיפוש פריט אומר לכם אם הוא קיים.
הבחירה בין השניים נוגעת לשאלה האם הערך שלצד כל פריט משמעותי:
פנו ל-קבוצה כאשר אין ערך השייך לצד כל פריט – כל שאכפת לכם הוא האם הפריט קיים, או שאתם משלבים קבוצות של פריטים ייחודיים באמצעות איחוד / חיתוך.
פנו ל-מילון כאשר כל פריט מזווג עם נתון שהחיפוש אמור לאחזר – מפת הגדרות, מטמון, מונה הממופתח לפי שם.
שני הסוגים חולקים חלק ניכר מהתחביר החיצוני, וזה המקור לרוב הבלבול. ההבדלים בבלוק אחד:
set |
dict |
|
|---|---|---|
מחזיק |
פריטים ייחודיים |
מפתחות ייחודיים, כל אחד עם ערך |
ליטרל מאוכלס |
|
|
ליטרל ריק |
|
|
בדיקת חברות |
|
|
אחזור ערך |
לא רלוונטי |
|
הוספת פריט |
|
|
איטרציה |
מניבה פריטים |
מניבה מפתחות (השתמשו ב- |
האסימטריה בין הליטרלים המאוכלסים לריקים היא המלכודת שראוי להדגיש:
סוגריים עם פריטים בתוכם –
{1, 2, 3}– הם ליטרל קבוצה; סוגריים עם זוגות מפתח-ערך –{"a": 1}– הם ליטרל מילון. המנתח מבחין ביניהם לפי מה שבפנים.סוגריים עם שום דבר בפנים –
{}– הם מילון ריק, לא קבוצה ריקה. מילונים הופיעו ראשונים; הליטרל הריק שייך להם. לקבוצה ריקה אין ליטרל סוגריים כלל, ויש לכתוב אותהset().
דפוס נפוץ כאשר רק המפתחות של מילון נקראים אי-פעם הוא לעבור לקבוצה – הדבר הופך את הכוונה לברורה ומקצץ את הערכים הלא בשימוש מהזיכרון.
2.10.3. הוספה והסרה¶
set.add()– מכניס פריט אחד.set.discard()– מסיר פריט אם הוא קיים, ולא עושה דבר אם אינו קיים.set.remove()– מסיר פריט; גורם ל-KeyErrorאם הוא חסר.set.clear()– מרוקן את הקבוצה.
s = {1, 2, 3}
s.add(4)
s.discard(99) # silent: 99 not in s
s.remove(2)
print(s)
פלט:
{1, 3, 4}
2.10.4. חברות¶
האופרטור in בודק חברות. בקבוצה הוא בקירוב בזמן קבוע ללא תלות בגודל – וזו הסיבה העיקרית לבחור בקבוצה על פני list כאשר כל שאתם צריכים הוא לשאול ”האם ערך זה נמצא שם“:
if "red" in colours:
print("colour is allowed")
list עם אותו תוכן תסרוק מההתחלה בכל פעם, מה שבסדר עבור עשרה פריטים אך איטי עבור עשרת אלפים.
2.10.5. פעולות על קבוצות¶
ניתן לשלב שתי קבוצות באמצעות הפעולות המתמטיות הרגילות. לכל אחת יש צורת אופרטור וצורת מתודה כאחד:
a | bאוa.union(b)– כל מה שנמצא באחת הקבוצות.a & bאוa.intersection(b)– רק מה שמופיע בשתיהן.a - bאוa.difference(b)– ב-aאך לא ב-b.a ^ bאוa.symmetric_difference(b)– באחת אך לא בשתיהן.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
פלט:
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
צורות האופרטור הן לקריאה בלבד; צורות המתודה מקבלות כל איטרבל בצד ימין, לא רק קבוצה נוספת (a.union([5, 6])). בחרו את מה שנקרא טוב יותר בהקשר.
2.10.6. מה יכול להיכנס לקבוצה¶
איברי קבוצה חייבים להיות ניתנים לגיבוב (hashable) – אותה מגבלה כמו עבור מפתחות dict. int, float, str, bool, bytes, ו-tuple (כאשר תוכנו עצמו ניתן לגיבוב) כולם עובדים. list ו-dict אינם עובדים; ניסיון להוסיף אחד מהם גורם ל-TypeError.
2.10.7. frozenset¶
set רגילה היא ברת-שינוי: כל קריאה ל-add / remove / discard משנה את האובייקט במקום. בריר-שינוי זו פוסלת אותה מלהיות ניתנת לגיבוב, ולכן קבוצה אינה יכולה לשמש כמפתח dict או כאיבר של קבוצה אחרת.
frozenset היא המקבילה הבלתי-משתנה. יש לה אותם חיפושים ואופרטורים (in, |, &, -, ^) כמו ל-set, אך ללא add / remove וללא מתודות המשנות אותה. מכיוון ששום דבר אינו יכול אי-פעם לשנות את תוכנה, הגיבוב של frozenset מוגדר היטב – ולכן היא כן ניתנת לגיבוב:
primary = frozenset({"red", "green", "blue"})
secondary = frozenset({"yellow", "purple", "orange"})
palettes = {
primary: "RGB",
secondary: "mixed",
}
print(palettes[primary])
פלט:
RGB
בנו frozenset מכל איטרבל – frozenset() למקרה הריק, frozenset(some_set) כדי לקחת תמונת בזק (snapshot) בלתי-משתנה של קבוצה קיימת:
snapshot = frozenset(s) # immutable copy of s
s.add("new") # snapshot does not change
שתי סיבות נפוצות לפנות אליה:
שימוש כמפתח מילון או כאיבר קבוצה. בכל מקום שערך יחיד אינו יכול לתפוס את מה שאתם צריכים,
frozensetשל ערכים יכולה – ”קבוצת המאפיינים הנתמכים על ידי דרייבר זה“, ”קבוצת הפינים שפרופיל זה משתמש בהם“.נעילת קבוע.
frozensetברמת המודול של שמות מורשים אינה יכולה להשתנות בטעות על ידי קורא;setרגילה כן. העדיפוfrozensetעבור כל דבר שאמור להיות לקריאה בלבד לאחר הבנייה.