מיון ב-Python

הדרך הקלה ביותר למיון היא באמצעות הפונקציה Sorted(list) הרשימה המקורית לא השתנתה.

  a = [5, 1, 4, 3]
  print(sorted(a))  ## [1, 3, 4, 5]
  print(a)  ## [5, 1, 4, 3]

הכי מקובל להעביר רשימה לפונקציה sorted() , אבל למעשה היא יכולה לקחת כל סוג של אוסף שניתן לחזור עליו. השיטה הישנה list.sort() היא שיטה חלופית שמפורטת בהמשך. נראה שקל יותר להשתמש בפונקציה sorted() בהשוואה ל-sort(), לכן מומלץ להשתמש ב-sorted().

אפשר להתאים אישית את הפונקציה sorted() באמצעות ארגומנטים אופציונליים. הארגומנט האופציונלי sorted() הפוכה=True, לדוגמה: sorted(list, האירוע=True), הופכת אותו למיון לאחור.

  strs = ['aa', 'BB', 'zz', 'CC']
  print(sorted(strs))  ## ['BB', 'CC', 'aa', 'zz'] (case sensitive)
  print(sorted(strs, reverse=True))   ## ['zz', 'aa', 'CC', 'BB']

מיון מותאם אישית באמצעות מקש=

למיון מותאם אישית מורכב יותר, הפונקציה Sorted() משתמשת ב-"key=" אופציונלי לציון פונקציית "key" שמשנה כל רכיב לפני ההשוואה. פונקציית המפתח מקבלת ערך אחד ומחזירה ערך אחד, וערך ה-proxy שהוחזר משמש להשוואות בתוך המיון.

לדוגמה, על ידי הצגת רשימה של מחרוזות, ציון key=len (הפונקציה המובנית ב-len() ) ממיין את המחרוזות לפי אורך, מהקצר אל הארוך ביותר. המיון קורא ל-len() עבור כל מחרוזת כדי לקבל את רשימת הערכים של האורך של שרת ה-proxy, ולאחר מכן ממיין לפי הערכים האלה של שרת ה-proxy.

  strs = ['ccc', 'aaaa', 'd', 'bb']
  print(sorted(strs, key=len))  ## ['d', 'bb', 'ccc', 'aaaa']

השיחות ממוינות לפי key=len

כדוגמה נוספת, ציון "str.lower" כפונקציית המפתח היא דרך לאלץ את המיון כך שיתייחס לאותיות רישיות ולאותיות קטנות באופן זהה:

  ## "key" argument specifying str.lower function to use for sorting
  print(sorted(strs, key=str.lower))  ## ['aa', 'BB', 'CC', 'zz']

אפשר גם להעביר את MyFn שלכם כפונקציית המפתח, באופן הבא:

  ## Say we have a list of strings we want to sort by the last letter of the string.
  strs = ['xc', 'zb', 'yd' ,'wa']

  ## Write a little function that takes a string, and returns its last letter.
  ## This will be the key function (takes in 1 value, returns 1 value).
  def MyFn(s):
    return s[-1]

  ## Now pass key=MyFn to sorted() to sort by the last letter:
  print(sorted(strs, key=MyFn))  ## ['wa', 'zb', 'xc', 'yd']

למיון מורכב יותר, כמו מיון לפי שם משפחה ולאחר מכן שם פרטי, תוכלו להשתמש בפונקציות itemgetter or attrgetter כמו:

  from operator import itemgetter

  # (first name, last name, score) tuples
  grade = [('Freddy', 'Frank', 3), ('Anil', 'Frank', 100), ('Anil', 'Wang', 24)]
  sorted(grade, key=itemgetter(1,0))
  # [('Anil', 'Frank', 100), ('Freddy', 'Frank', 3), ('Anil', 'Wang', 24)]

  sorted(grade, key=itemgetter(0,-1))
  #[('Anil', 'Wang', 24), ('Anil', 'Frank', 100), ('Freddy', 'Frank', 3)]

שיטת המיון()

כחלופה ל-sorted(), שיטת sort() ברשימה ממיינת רשימה זו בסדר עולה, למשל list.sort() . שיטת המיון() משנה את הרשימה הבסיסית ומחזירה None, לכן יש להשתמש בה כך:

  alist.sort()            ## correct
  alist = blist.sort()    ## Incorrect. sort() returns None

הסיבה שלמעלה היא אי-הבנה נפוצה בקשר ל-sort() -- הוא *לא מחזיר* את הרשימה הממוינת. חובה לקרוא לשיטה sort() ברשימה. היא לא פועלת באף אוסף שניתן לספירה (אבל הפונקציה sorted() שלמעלה פועלת על כל דבר). שיטת מיון (()) ישנה יותר מהפונקציה sorted() , לכן סביר להניח שהיא תופיע בקוד ישן יותר. ל-method() אין צורך ליצור רשימה חדשה, לכן היא יכולה לפעול קצת יותר מהר אם הרכיבים למיון כבר נמצאים ברשימה.

טופלס

טפל הוא קבוצת אלמנטים בגודל קבוע, כמו קואורדינטה (x, y). Tuples הם כמו רשימות, אלא שלא ניתן לשנות את גודלם (הצימודים לא תמיד ניתנים לשינוי מאחר שאחד מהרכיבים הכלולים בו יכול להיות ניתן לשינוי). Tuples ממלאים מעין תפקיד "struct" ב-Python -- דרך נוחה להעביר חבילה קטנה לוגית וקבועה של ערכים. פונקציה שצריכה להחזיר ערכים מרובים יכולה להחזיר רק שניים מהערכים. לדוגמה, אם אני רוצה לקבל רשימה של קואורדינטות תלת ממד, הייצוג הטבעי של python יהיה רשימה של צמדים, כאשר כל tuple הוא בגודל 3 ובו קבוצה אחת (x, y, z).

כדי ליצור +tuple, יש לרשום את הערכים בסוגריים ולהפריד ביניהם באמצעות פסיקים. הצירוף ה "ריק" הוא פשוט זוג ריק של סוגריים. הגישה לרכיבים ב-tuple היא בדיוק כמו רשימה -- len(), [ ], for, in, וכו'. כולם פועלים באותו אופן.

  tuple = (1, 2, 'hi')
  print(len(tuple))  ## 3
  print(tuple[2])    ## hi
  tuple[2] = 'bye'  ## NO, tuples cannot be changed
  tuple = (1, 2, 'bye')  ## this works

כדי ליצור tuple בגודל 1, יש להוסיף פסיק אחרי הרכיב הבודד.

  tuple = ('hi',)   ## size-1 tuple

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

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

  (x, y, z) = (42, 13, "hike")
  print(z)  ## hike
  (err_string, err_code) = Foo()  ## Foo() returns a length-2 tuple

הצגת רשימה של להשלים (אופציונלי)

הבנת רשימות היא תכונה מתקדמת יותר. בחלק מהמקרים היא נחמדה, אבל לא נדרשת לתרגילים, ואין צורך ללמוד משהו בהתחלה (כלומר, אפשר לדלג על קטע זה). הבנת הרשימה היא דרך קומפקטית לכתוב ביטוי שמתרחב לרשימה שלמה. נניח שיש לנו רשימה של מספרים [1, 2, 3, 4]. לפניכם הבנת הרשימה כדי לחשב רשימה של הריבועים שלה [1, 4, 9, 16]:

  nums = [1, 2, 3, 4]

  squares = [ n * n for n in nums ]   ## [1, 4, 9, 16]

התחביר הוא [ expr for var in list ] -- ה-for var in list נראה כמו לולאה רגילה, אך ללא הנקודתיים (:). ה-expr שמשמאלו מוערך פעם אחת עבור כל רכיב, כדי לקבל את הערכים של הרשימה החדשה. לפניכם דוגמה עם מחרוזות, שבה כל מחרוזת משתנה לאותיות רישיות ומצרפים '!!!':

  strs = ['hello', 'and', 'goodbye']

  shouting = [ s.upper() + '!!!' for s in strs ]
  ## ['HELLO!!!', 'AND!!!', 'GOODBYE!!!']

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

  ## Select values <= 2
  nums = [2, 8, 1, 6]
  small = [ n for n in nums if n <= 2 ]  ## [2, 1]

  ## Select fruits containing 'a', change to upper case
  fruits = ['apple', 'cherry', 'banana', 'lemon']
  afruits = [ s.upper() for s in fruits if 'a' in s ]
  ## ['APPLE', 'BANANA']

תרגיל: list1.py

כדי לתרגל את החומר בקטע הזה, נסה מאוחר יותר לפתור בעיות ב-list1.py שמשתמשות במיון ובטיפולים (בתרגילים הבסיסיים).