איטרטורים הם אובייקטים שמייצגים זרם של נתונים. הם מממשים את פרוטוקול האיטרטורים, שכולל שתי מתודות עיקריות:
- __iter__() – מחזירה את האובייקט עצמו כאיטרטור
- __next__() – מחזירה את הפריט הבא, או מעלה חריגת StopIteration כאשר אין פריטים נוספים
בפייתון, כל אובייקט שאפשר לעבור עליו בלולאת for הוא או איטרטור בעצמו, או שיש לו מתודה __iter__ שמחזירה איטרטור. מבני הלולאה של פייתון, כמו גם פונקציות מובנות רבות (כמו sum(), list() ועוד), משתמשים בפרוטוקול הזה כדי לעבור על איברים.
אמנם ניתן לממש איטרטורים באופן ידני (כמו בדוגמת הפיבונאצ’י שראינו קודם), אבל פייתון מספקת כלי נוח יותר ליצירת איטרטורים – גנרטורים.
גנרטור הוא סוג מיוחד של איטרטור, שנכתב כפונקציה המשתמשת בפקודת yield כדי להחזיר סדרת ערכים בצורה "עצלנית" (lazy) – כלומר, כל ערך מחושב רק כשמבקשים אותו.
כאשר קוראים לפונקציית גנרטור, היא מחזירה אובייקט גנרטור שמממש אוטומטית את __iter__ ו-__next__. כל פעם שהפונקציה מגיעה ל-yield, היא עוצרת את הביצוע, שומרת את המצב, וממשיכה מהמקום הזה בקריאה הבאה.
דוגמה – פונקציית גנרטור
הנה גרסה שקולה לרצף פיבונאצ'י באמצעות גנרטור:
הגנרטור fib generator מחזיר רצף אינסופי של מספרי פיבונאצ'י, בדומה למחלקה שהצגנו קודם – אבל עם הרבה פחות קוד. כל קריאה ל-next(fib) מריצה את הפונקציה עד לפקודת yield הבאה, ואז עוצרת את הביצוע.
ביטויי גנרטור (generator expressions) מציעים תחביר מקוצר, בדומה לרשימות comprehension – אך במקום להחזיר רשימה, הם מחזירים גנרטור.
למשל:
python
squares = (x*x for x in range(10))
יוצר גנרטור שמחזיר את ריבועי המספרים מ-0 ועד 81.
שיטה זו חסכונית בזיכרון במיוחד כאשר עובדים עם רצפים גדולים, כי הפריטים מחושבים "תוך כדי תנועה" ולא נשמרים כולם בזיכרון.
גנרטורים ואיטרטורים הם כלים בסיסיים לעבודה עם זרמי מידע (streams), מערכי נתונים גדולים, או כאשר רוצים להשתמש בהערכה עצלה (lazy evaluation) בפייתון.
המודול itertools שבספריית התקן מספק כלים רבים ליצירה ושילוב של איטרטורים – כולל דפוסים מתקדמים של איטרציה, כמו מונים אינסופיים, קומבינטוריקה, חלוקה לקבוצות (batching) ועוד.
שווה גם להכיר את מנגנון ההאצלה בגנרטורים באמצעות התחביר yield from (שהוצג ב-PEP 380).
תחביר זה מאפשר לגנרטור להחזיר את כל הערכים מתוך iterable אחר או גנרטור תת-משני, ובכך מקל על חיבור בין גנרטורים שונים.