خانه / آموزش‌ها / آموزش ایتراتورها و جنریتورها در پایتون

آموزش ایتراتورها و جنریتورها در پایتون

🐍 HomeOfPython
|
📅 1404/10/18

سطح مقدماتی (Beginner Level)

در پایتون، بسیاری از اوقات ما روی لیست‌ها یا رشته‌ها حلقه می‌زنیم، اما تا به حال فکر کرده‌اید که این فرآیند دقیقاً چگونه کار می‌کند؟ مفاهیم Iterator (تکرارگر) و Generator (تولیدکننده) پاسخ این سوال هستند و به شما کمک می‌کنند کدهای بهینه‌تر و تمیزتری بنویسید.

۱. تفاوت Iterable و Iterator

برای درک این موضوع باید دو مفهوم را از هم جدا کنیم:

  1. Iterable (قابل تکرار): هر چیزی که بتوان روی آن حلقه for زد (مثل لیست، رشته، تاپل).
  2. Iterator (تکرارگر): شیئی که وظیفه دارد داده‌ها را یکی‌یکی به ما تحویل دهد و موقعیت فعلی را به یاد داشته باشد.

زمانی که شما یک حلقه for می‌نویسید، پایتون در پشت صحنه آن شیء را به یک Iterator تبدیل می‌کند.

تفاوت Iterable و Iterator

مثال ۱: تبدیل لیست به ایتراتور

ما می‌توانیم با تابع iter() یک لیست را به ایتراتور تبدیل کنیم و با next() مقادیر را یکی‌یکی بگیریم.

Python
python
# ساختار کلی (بدون اجرا)
# وقتی روی شیء next() صدا زده می‌شود، مقدار بعدی برگردانده می‌شود
iterator = iter(some_collection)
item = next(iterator)

۲. معرفی جنریتورها (Generators) و دستور yield

جنریتورها روشی ساده‌تر برای ساختن ایتراتورها هستند. به جای اینکه تمام مقادیر را در یک لیست ذخیره کنیم (که حافظه رم را اشغال می‌کند)، یک تابع می‌نویسیم که هر بار یک مقدار را تولید (yield) می‌کند و سپس متوقف می‌شود تا دوباره صدا زده شود.

تفاوت کلیدی: return تابع را کلاً تمام می‌کند، اما yield تابع را موقتاً متوقف می‌کند و وضعیت آن را حفظ می‌کند.

مثال ۲: ساخت اولین جنریتور

در اینجا تابعی داریم که اعداد ۱ تا ۳ را تولید می‌کند.

Python

مثال ۳: تولید اعداد زوج

فرض کنید می‌خواهیم اعداد زوج را تولید کنیم. با جنریتور نیازی نیست لیست بسازیم.

Python

سطح پیشرفته (Professional Level)

در سطح حرفه‌ای، استفاده از ایتراتورها و جنریتورها مستقیماً روی پرفورمنس (Performance) و مدیریت حافظه (Memory Management) تاثیر می‌گذارد. همچنین ساخت ایتراتورهای سفارشی با کلاس‌ها برای کنترل دقیق رفتار پیمایش ضروری است.

۱. ساخت کلاس Iterator سفارشی

برای اینکه یک کلاس قابلیت استفاده در حلقه for را داشته باشد، باید دو متد جادویی (Dunder Methods) را پیاده‌سازی کند:

  1. __iter__: خود شیء را برمی‌گرداند.
  2. __next__: مقدار بعدی را برمی‌گرداند و اگر داده تمام شد، خطای StopIteration را ایجاد می‌کند.

مثال ۴: کلاس توان‌های عدد ۲

این کلاس رفتاری شبیه به range دارد اما توان‌های ۲ را برمی‌گرداند.

Python
python
# ساختار کلی کلاس ایتراتور (Snippet)
class CustomIterator:
    def __iter__(self):
        return self
    
    def __next__(self):
        # منطق تولید داده
        pass

۲. مقایسه مصرف حافظه (List vs Generator)

یکی از مهم‌ترین دلایل استفاده از جنریتورها در پروژه‌های بزرگ (Big Data)، مصرف بهینه حافظه است.

  • List: تمام اعداد را همزمان در رم بارگذاری می‌کند.
  • Generator: در هر لحظه فقط یک عدد را در رم نگه می‌دارد.

مقایسه حافظه

مثال ۵: Generator Expressions

شبیه به List Comprehension است اما با پرانتز () ساخته می‌شود و یک جنریتور برمی‌گرداند نه لیست.

Python

۳. دنباله‌های بی‌نهایت (Infinite Sequences)

چون جنریتورها داده‌ها را "به‌صورت تنبل" (Lazily) تولید می‌کنند، می‌توانیم دنباله‌هایی بسازیم که هیچ‌وقت تمام نمی‌شوند (مثل سنسوری که مدام داده می‌فرستد) بدون اینکه حافظه پر شود.

مثال ۶: فیبوناچی بی‌نهایت

این تابع تا ابد عدد فیبوناچی تولید می‌کند. ما باید خودمان شرط توقف بگذاریم.

Python

۴. استفاده از yield from

زمانی که می‌خواهید از درون یک جنریتور، یک جنریتور دیگر را صدا بزنید، به جای حلقه زدن روی آن، از yield from استفاده کنید. این کار کد را تمیزتر می‌کند و ارتباط مستقیم بین caller و sub-generator برقرار می‌کند.

Python