3.3 למבדה, דקורטור, וגנרטור הרצאה
למבדה, map, filter ו - sorted¶
פונקציות למבדה:
- פונקציות למבדה או באנגלית "lambda" הן פונקציות שניתן להגדיר בלי שם.
אנחנו מגדירים אותם בשימוש המילה השמורה lambda.
- בגלל שלפונקציות למבדה אין שם, הן נקראות פונקציות אנונימיות.
- בדרך כלל למבדה משמש אותנו לטווח קצר- כלומר, למען פעולות פשוטות:
- כאשר אנחנו מגדירים פונקצית למבדה אנחנו קודם מציינים את הפרמטרים של הפונקציה על ידי הפרדתם בעזרת פסיקים ונקודותיים. אחרכך אנחנו כותבים את הביטוי חזרה של הפונקציה.
- הנה דוגמה לlambda:
- כאשר נעביר לפונקציה func מספר, נקבל אותו ועוד 1.
למה למבדה?¶
פונקציות Lambda משמשות ליצירת פונקציות אנונימיות, קטנות וחד פעמיות. הם שימושיים במיוחד בתרחישים שבהם אתה צריך פונקציה פשוטה למשך זמן קצר ולא רוצה להגדיר רשמית פונקציה בעלת שם באמצעות def.
פעולות נפוצות עם lambda¶
פונקציה מובנית map
- הפונקציה המובנית map מבצעת פעולה שאנחנו מגדירים לה על כל האיברים ברשימה שאנחנו נותנים לה.
- בדרך כלל אנחנו מעבירים לפונקציה map, כפרמטר, איזה פעולה לעשות על הרשימה עם lambda.
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # Will output: 1, 4, 9, 16, 25
- הפעולה הבאה תחשב את החזקה של כל המספרים ברשימה.
- שימו לב שהפונקציה
map קודם מקבלת פונקציה (העברנו lambda), ואחרכך מקבלת איטרבל (העברנו רשימה).- הפונקציה
map שימושית כאשר אנחנו רוצים לבצע פעולה מסויימת (הlambda) על רשימה מסויימת. (יכול להיות כל איטרבל)
פונקציה מובנית filter
- מוציא איברים מרשימה בהתבסס על תנאי.
- בדרך כלל אנחנו מעבירים לפונקציה filter כפרמטר, תנאי שמטרתו להוציא איברים מסויימים מרשימה אם הוא נכון.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)
- בדומה למעלה הוצאנו את כל המספרים שמתחלקים ב2. (באמצעות הפעולה מודולו %)
- הפונקציה filter דומה לmap, רק שמטרתה להוציא איברים מהרשימה.
פונקציה מובנית sorted
- ממיינת רשימה בהתבסס על "מפתח מיון".
- מפתח מיון מגדיר איך לסדר את הרשימה, בדרך כלל נשתמש בlambda כדי להגדיר את המפתח מיון.
books = [
{'title': 'The Great Gatsby', 'year': 1925},
{'title': 'To Kill a Mockingbird', 'year': 1960},
{'title': '1984', 'year': 1949},
{'title': 'The Catcher in the Rye', 'year': 1951},
]
sorted_books = sorted(books, key=lambda book: book['year'])
- יסדר את הספרים לפי השנה שלהם.
מתי להשתמש בלמבדה¶
- פונקציות קצרות מועד: כאשר יש צורך בפונקציה רק לזמן קצר או בהיקף מוגבל.
- פונקציות כפרמטר: פונקציות שמקבלות פונקציות כפרמטר:
map,filter,sorted
דוקרטורים - Decorators¶
דקורטורים: (קישוטים)
דקורטור בפייתון הוא תבנית עיצוב המאפשרת להוסיף פונקציונליות לפונקציה או למתודה קיימת בצורה דינמית, בלי לשנות את הקוד שלה. דקורטורים בפייתון הם למעשה פונקציות שמקבלות פונקציה כארגומנט ומחזירות פונקציה חדשה עם פונקציונליות נוספת.
- בפשטות: דקרטור זה פונקציה שמקבלת פונקציה ומחזירה פונקציה.
- מטרת פונקצית דקורטור היא לשנות את ההתנהגות של פונקציה קיימת, הנה דוגמה לדקורטור:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
- ניתן לראות שכתבנו פונקציה בשם my_decorator, שמקבלת כפרמטר פונקציה (func)
- הדקורטור מגדיר פונקציה חדשה ממש בתוכו בשם wrapper, במקרה הזה אפשר לראות שהפונקציה החדשה שהוא הגדיר (wrapper) פשוט עושה 2 print-ים ומריץ את הפונקציה שהוא קיבל כפרמטר.
- אחרי שהגדרנו את הפונקציה wrapper, אנחנו מחזירים את הפונקציה החדשה שהגדרנו.
הפונקציה (דקורטור) שכתבנו מקבלת פונקציה, יוצרת פונקציה חדשה שעושה print-ים וקורא לפונקציה שהיא קיבלה, ולבסוף הפונקציה (דקורטור) מחזירה את הפונקציה החדשה שהיא יצרה.
- וכך, דקורטור זה פונקציה שמקבלת פונקציה ומחזירה פונקציה חדשה, שונה.
- דמיינו דקורטור כקישוט, מטרתו הוא "לקשט" פונקציות שהוא מקבל ולהוסיף להן עוד קוד.
- ניתן לקשט פונקציות עם הדקורטור שלנו עם התו
@
- במקרה הזה שנינו את ההתנהגות של הפונקציות
say_hello, ו -say_byeבאמצעות הדקורטורmy_decorator. - בעתיד נלמד עוד על דקורטורים
דקורטור לפונקציה עם פרמטר¶
- כדי להגדיר דקורטור לפונקציה שמקבלת פרמטרים, נציין אותם בהגדרה של הדקרטור, הנה דוגמה לדקורטור כזה:
- הדקורטור הבא מקבל פונקציה שמקבלת פרמטר, ומחזיר פונקציה שמקבלת פרמטר.
- נסו לעקוב אחר הקוד בעצמכם ולהבין מה הוא עושה.
גנרטורים - Generators¶
- המון פעמים בפייתון, אנחנו ניצור איזשהו ליסט שעלול להיות גדול מאוד, ואחרכך נבצע עליו פעולות, כמו למשל בדוגמה הבאה:
- הבעיה, שהקוד הזה יהיה מאוד איטי, בגלל שקודם כל אנחנו צריכים ליצור רשימה שעלולה להיות גדולה מאוד, ורק אחרכך אנחנו נוכל לבצע עלייה פעולות, כמו למשל להדפיס את כל האיברים שלה.
- במקרה הזה, דוגמה לקוד יותר יעיל יהיה: בזמן שאנחנו יוצרים איברים ברשימה, נדפיס אותם אחד אחרי השני.
- כך שבזמן שאנחנו יוצרים את כל האיברים ברשימה, נבצע עלייה את הפעולות. למשל, כאשר אנחנו יוצרים רשימה, נדפיס כל איבר שיצרנו.
- קוד כזה, יכול להיראות כך:
- הקוד הזה יותר מהיר מהקוד השני, למורות שהם עושים בדיוק את אותו הדבר.
-
הבעיה בקוד הזה שהוא מבולגן יותר, כי באותה פונקציה אנחנו גם יוצרים את הרשימה וגם עושים עלייה פעולה כמו הדפסה, וזה לא מאורגן.
בכדי לפתור את הבעיה הזו - יש לנו גנרטורים. -
גנרטור זה type מיוחד, דומה לרשימה, שיוצר איטראבל (תזכורת: type שניתן לעבור עליו עם לולאה - למשל רשימה), בזמן שאנחנו מבצעים עליו פעולות.
- דמיינו רשימה שיוצרת את עצמה בזמן שאנחנו עוברים עלייה עם לולאה ומדפיסים את האיברים שלה, זה דוגמה לגנרטור.
- מגדירים גנרטור עם פונקציה, זה פונקציה מיוחדת שבמקום
returnעושהyeild.
- למעשה, כאשר הקוד שלנו עובר עם for על הגנרטור squares_numbers, בכל יצירה של איבר, הוא גם מדפיס אותו.
- כלומר, הקוד שלנו יקפוץ בין הfor שיש בפונקציה square_numbers לfor שיש למטה סיכרונית.
- שימו לב, רשימה היא לא גנרטור, אז אם אנחנו נעשה type casting בין גנרטור לרשימה, זה פשוט יבטל את הגנרטור ויהפוך אותו לרשימה.
- עוד נקודה מעניינת: range הוא גנרטור, אז הוא מאוד יעיל.
נקודה חשובה¶
- השימוש בנושאים שלמדנו בהרצאה אינו נפוץ במיוחד.
- אך חשוב להכיר את הנושאים במקרה ותתקלו בקוד שמשתמש בנושאים שלמדנו, או שתהיו במצב שבו הנושאים שלמדנו יכולים ליעל לכם את העבודה.