מה זה generator של פייתון? ואיך משתמשים בו

מחבר:
בתאריך:

האם קרה לך פעם שרצית להריץ לולאה מספר רב של פעמים אבל המחשב קרס באמצע. אם זה המצב או שאתה פשוט סקרן לדעת מהו generator של פייתון אז הגעת למקום הנכון.

generator של פייתון הם שימושיים אם צריך להריץ את אותה הפונקציה מספר רב של פעמים. לדוגמה, ביצוע פעולה על מספר רב של רשומות אותם שולפים ממסד הנתונים או כשרוצים לבצע פעולה מתמטית על סדרה ארוכה של מספרים. במקום שפונקציה רגילה תרוץ על כל הנתונים, generator יאפשר למחשב לעבוד על פריט אחד בכל פעם מה שחוסך מקום בזיכרון ומפחית את העומס על המחשב.

Python generators tutorial

 

איך הופכים פונקציה רגילה ל-generator?

המפתח להפיכת פונקציה ל-generator הוא שימוש בהצהרת-yield במקום ב-return.

נדגים זאת על ידי הפיכת פונקציה רגילה ל-generator.

נכתוב פונקציה רגילה שמוסיפה X אחוזים לכל פריט במערך המספרים שנעביר אליה:

def change_by_precent(numbers, percent):
   modified_numbers = []
   for number in numbers:
       modified_numbers.append(number*(1+percent/100))
   return modified_numbers
  
numbers = [10, 20, 30]
res = change_by_precent(numbers, 10)
  • הפונקציה change_by_percent מקבלת שני פרמטרים: מערך מספרים numbers ושיעור השינוי באחוזים percent.
  • הפונקציה מריצה לולאת for על מערך המספרים, מבצעת חישוב מתמטי על כל פריט, ומוסיפה את התוצאה למערך modified_numbers אותו הוא מחזירה.

נדפיס את התוצאה:

print(res)
[11.0, 22.0, 33.0]

נהפוך את הפונקציה ל-generator על ידי כך שנחליף את ה-return ב-yield, ונפטר מהמערך:

def change_by_precent_generator(numbers, percent):
   for number in numbers:
       yield number*(1+percent/100)

נעביר לפונקציה change_by_percent_generator את מערך המספרים ואת שיעור השינוי באחוזים, ונאחסן את התוצאה במשתנה res_gen:

numbers = [10, 20, 30]
res_gen = change_by_precent_generator(numbers, 10)
print(res_gen)

מה התוצאה של ה-generator?

<generator object change_by_precent_generator at 0x7f921b4a9c80>

אובייקט generator. מייד נראה איך לעבוד איתו.

אבל ראשית נסביר מה עשינו:

  • ה-generator מחזיר את תוצאת החישוב עבור כל פריט בנפרד באמצעות הצהרת yield שמחליפה את return.
  • מכיוון שהפונקציה מנפקת בכל פעם תוצאה אחת אין צורך לאסוף את התוצאות לתוך מערך, ולפיכך נפטרנו ממנו.

 

איך שולפים את המידע מה-generator?

ה-generator לא מחזיק את כל התוצאות בזיכרון. במקום זה, בכל פעם שרוצים שהוא יבצע פעולה על הפריט הבא בתור צריך לקרוא לו.

אפשר לקרוא ל-generator באמצעות הפונקציה next() כדי לקבל את התוצאה הבאה בתור.

נקרא ל-generator עבור הפריט הראשון:

print(next(res_gen))

התוצאה:

11.0

נקרא לו שוב עבור הפריט השני:

print(next(res_gen))
22.0

והשלישי:

print(next(res_gen))
33.0

אבל אם ננסה לקרוא ל-generator בפעם הרביעית:

print(next(res_gen))

נקבל שגיאה בגלל שאין פריט רביעי:

Traceback (most recent call last):
  File "main.py", line 14, in 
    print(next(res_gen))
StopIteration

השגיאה StopIteration היא תוצאה ישירה של העובדה שהעברנו מערך של 3 פריטים ואז ניסינו לשלוף תוצאה רביעית שלא קיימת. דרך אחרת להגיד את זה היא שה-generator מותש exhausted.

במציאות, לא משתמשים בפונקציה next() כדי למצות את התוצאות מתוך generator אלא בלולאה:

for number in res_gen:
   print(number)

התוצאה:

11.0
22.0
33.0
  • זה עובד בגלל שהלולאה קוראת לפונקציה next() מאחורי הקלעים.

נסכם את מה שראינו עד עכשיו. בעוד הצהרת return מחייבת את הפונקציה לבצע את הפעולה על כל המערך אותו היא מחזירה, הצהרת yield מפסיקה את הביצוע אחרי פעם אחת, מחזירה את תוצאת הפעולה, ואז מחכה לקריאה הבאה.

 

תחביר מקוצר לכתיבת generators באמצעות generator expression

אחד הפינוקים שמציעה שפת הפייתון הוא list comprehension שפועל כמו לולאות על רשימות בתחביר קומפקטי וקריא יותר. אם אינך מכיר את הנושא אז אתה מוזמן לקרוא את המדריך על list comprehensions.

לדוגמה, אפשר לכתוב את הפונקציה להוספת אחוזים למספרים באמצעות תחביר קומפקטי בהרבה:

percent = 10
res = [number*(1+percent/100) for number in numbers]

התוצאה:

[11.0, 22.0, 33.0]

רשימה.

נחליף את הסוגריים המרובעים בעגולים כדי שהתוצאה תהיה generator במקום רשימה:

percent = 10
res_gen = (number*(1+percent/100) for number in numbers)

התוצאה:

<generator object <genexpr> at 0x7f11ed845d60>

אובייקט generator.

 

מתי להשתמש ב-generator?

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

 

סיכום

  • בפייתון נשתמש ב-yield במקום ב-return כדי להפוך פונקציה ל-generator.
  • נשתמש בלולאה כדי לשלוף את התוצאות אחת-אחת מתוך אובייקט generator.
  • השימוש ב-generator מפחית את צריכת המשאבים של המחשב מכיוון שאת התוצאות שולפים אחת-אחת במקום שהפונקציה תעשה את כל החישובים בבת אחת, ותאכלס את זיכרון המחשב בכל התוצאות. במקרה של רשימות קצרות זה לא משנה אבל הטכניקה הופכת משמעותית עבור מספר גדול של פריטים.

לכל המדריכים בסדרה ללימוד פייתון

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 3 + 4