תחי ישראל - אין לנו ארץ אחרת

תחי ישראל -אין לנו ארץ אחרת

מה זה 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 מפסיקה את הביצוע אחרי פעם אחת, מחזירה את תוצאת הפעולה, ואז מחכה לקריאה הבאה.

 

גנרטורים ורשימות

דרך נוספת לעבוד עם תוצאה של גנרטור היא להמיר אובייקט generator לרשימה:

numbers = [10, 20, 30]

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

# Cast into a list
print(list(res_gen))

התוצאה:

[11.0, 22.0, 33.0]
  • רשימה במקום אובייקט גנרטור

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


# Cast into a list and print for the first time
print(list(res_gen)) # [11.0, 22.0, 33.0]

# Cast into a list and print for the second time
print(list(res_gen)) # []
  • אם בפעם הראשונה שהמרנו לרשימה קיבלנו את רשימת התוצאות אז בפעם השנייה קיבלנו רשימה ריקה בגלל שהתשנו את הגנרטור.

אז מה עושים? מציבים את הרשימה שקיבלנו מן הגנרטור לתוך משתנה, ומשתמשים בהמשך במשתנה זה במקום בגנרטור.

נחזור לדוגמה שלנו:

numbers = [10, 20, 30]

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

# Cast into a list and save the result for later use
list_gen = list(res_gen)

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

 

תחביר מקוצר לכתיבת 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 כוכבים

 

 

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

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

שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.

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

השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.

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

 

 

ענה על השאלה הפשוטה הבאה כתנאי להוספת תגובה:

דג למים הוא כמו ציפור ל...?

 

תמונת המגיב

אור בתאריך: 18.09.2022

אני חושב שפספסת את הרעיון המרכזי מאחורי yield , זה עוצר את התוכנית ונותן למערכת ההפעלה להריץ תוכניות ברקע ,בד"כ משתמשים בזה כשעובדים עם תוכנית שרצה ברקע ולא רוצים שהיא תתקע את המחשב