מה זה lambda בפייתון - פונקציות אנונימיות בלי כאב ראש¶
בואו נדבר בכנות. פייתון היא שפה שמתגאה בקריאות, ואז יש בה את lambda - שורה מוזרה שנראית כאילו מישהו דחס פונקציה שלמה למקום שלא נועד לה. בפועל זה כלי פשוט מאוד, וברגע שתבינו למה הוא קיים, תפסיקו לפחד ממנו.
מה זה בעצם lambda¶
lambda היא דרך לכתוב פונקציה קטנה, בלי שם, בשורה אחת. במקום def רגיל:
אפשר לכתוב:
התחביר הוא lambda ואז הפרמטרים, נקודתיים, ואז ביטוי אחד שהתוצאה שלו מוחזרת אוטומטית - בלי מילת return, בלי בלוק גוף רגיל. יש לה בדיוק ביטוי אחד, לא רצף שלם של פקודות.
חשוב להבין - lambda לא עושה שום דבר שפונקציה רגילה לא יכולה לעשות. זה לא תכונה חזקה יותר, זה רק תחביר קצר יותר למקרה מאוד ספציפי - פונקציה קטנטנה שרוצים להעביר כפרמטר, בלי לתת לה שם ולתפוס איתה שורה נפרדת.
שימוש נפוץ - sorted עם key¶
הדוגמה הכי שכיחה ל-lambda היא כשצריך למיין רשימה לפי קריטריון מותאם אישית. נניח רשימת מילונים שמייצגים אנשים, ורוצים למיין לפי גיל:
people = [
{"name": "דנה", "age": 34},
{"name": "עומר", "age": 21},
{"name": "נועה", "age": 45},
]
sorted_people = sorted(people, key=lambda person: person["age"])
הפרמטר key מצפה לפונקציה שמקבלת איבר ומחזירה את הערך שלפיו ממיינים. במקום להגדיר פונקציה נפרדת עם def רק בשביל השורה הזו, נוח לזרוק lambda ישירות בתוך הקריאה.
שימוש עם map ו-filter¶
שני מקומות נוספים שבהם lambda מופיע הרבה - map, שמפעיל פונקציה על כל איבר ברצף, ו-filter, שמשאיר רק איברים שעומדים בתנאי:
numbers = [1, 2, 3, 4, 5, 6]
doubled = list(map(lambda n: n * 2, numbers))
evens = list(filter(lambda n: n % 2 == 0, numbers))
שווה לציין - ברוב המקרים האלה, רשימת הבנה עושה בדיוק את אותו דבר בצורה קריאה יותר, למשל [n * 2 for n in numbers] במקום map עם lambda. אז אל תחשבו על lambda כדרך היחידה או המומלצת תמיד - היא רק אחת האפשרויות.
עוד שימוש שכיח - מיון עם reverse ומפתחות כפולים¶
לפעמים רוצים למיין לפי כמה שדות בבת אחת, למשל קודם לפי מחלקה ואז לפי גיל בתוך אותה מחלקה. גם כאן lambda נוחה, כי אפשר להחזיר מהביטוי שלה tuple, ופייתון יודע להשוות tuple איבר אחרי איבר:
students = [
{"name": "רון", "grade": "יא", "age": 17},
{"name": "מיכל", "grade": "י", "age": 16},
{"name": "אורי", "grade": "יא", "age": 16},
]
sorted_students = sorted(students, key=lambda s: (s["grade"], s["age"]))
אפשר גם להפוך את סדר המיון עם reverse=True, בלי לגעת ב-lambda עצמה:
זה עדיין אותו רעיון - lambda מחזירה ערך יחיד, ו-sorted עושה את כל העבודה של ההשוואה בעצמה.
מתי lambda עוזרת ומתי היא מזיקה¶
וכאן החלק שהרבה מדריכים מדלגים עליו. lambda מעולה כשהלוגיקה בפנים היא באמת קצרה וברורה - חילוץ שדה, השוואה פשוטה, חישוב בסיסי. ברגע שהיא לא, אל תכריחו אותה להישאר בשורה אחת:
# זה כבר עבר את הגבול
result = sorted(data, key=lambda x: (x["score"] * 0.7 + x["bonus"] * 0.3) if x["active"] else -1)
מישהו שיקרא את השורה הזו בעוד חצי שנה, כולל אתם עצמכם, יצטרך לעצור ולפענח אותה במקום פשוט להבין אותה. במקרה כזה, def רגיל עם שם משמעותי הוא הבחירה הנכונה, גם אם זה עוד שתיים-שלוש שורות קוד:
def score_key(person):
if not person["active"]:
return -1
return person["score"] * 0.7 + person["bonus"] * 0.3
result = sorted(data, key=score_key)
הכלל הפשוט - אם צריך לעצור ולחשוב פעמיים כדי להבין מה ה-lambda עושה, זה סימן שהיא צריכה להפוך לפונקציה רגילה עם שם. שם טוב לפונקציה הוא תיעוד בחינם, ו-lambda אנונימית מוותרת בדיוק על זה.
טעות נפוצה אצל מתחילים היא להשתמש ב-lambda כי היא "נראית מקצועית", גם כשפונקציה רגילה הייתה ברורה יותר. אם אתם עוד מתלבטים מתי def עדיף ומתי lambda מספיקה, הקורס התכנות הבסיסי שלנו עובר על פונקציות מהיסוד ובונה את התחושה הזו בהדרגה.
לסיכום¶
lambda היא לא קסם ולא סימן להיכרות עמוקה עם פייתון, היא רק קיצור דרך לפונקציה קטנה בלי שם. השתמשו בה כשהיא באמת מקצרת ומבהירה קוד, בעיקר בתוך key, map ו-filter, ותוותרו עליה בלי בושה ברגע שהלוגיקה מתחילה להסתבך.