درک عمیق __iter__ و __next__ در پایتون
در پایتون، مفهوم تکرشپذیری (Iterability) یکی از ارکان اصلی کار با دادهها و ساختارهای مختلف است. حلقههای for، توابع map()، filter() و بسیاری از ابزارهای دیگر، بر پایه این مفهوم بنا شدهاند. اما پشت پرده این قابلیتها چه اتفاقی میافتد؟ در این مقاله، به بررسی عمیق دو متد ویژه __iter__() و __next__() میپردازیم که اساس پیادهسازی تکرشپذیری در پایتون را تشکیل میدهند. این مقاله در سطح ‘مفاهیم متوسط’ پایتون قرار میگیرد و برای کسانی که با مفاهیم پایه پایتون آشنا هستند، مناسب است.
تکرشپذیری چیست؟
یک شیء تکرشپذیر (Iterable) شیئی است که میتوان بر روی عناصر آن پیمایش کرد. به عبارت دیگر، میتوان از آن در یک حلقه for استفاده کرد. مثالهای رایج از اشیاء تکرشپذیر شامل لیستها، تاپلها، دیکشنریها، مجموعهها و رشتهها هستند. اما تکرشپذیری یک ویژگی ذاتی نیست که به طور خودکار به همه اشیاء داده شود. بلکه باید با پیادهسازی متدهای خاصی، یک شیء را تکرشپذیر کرد.
نقش __iter__()
متد __iter__() مسئول بازگرداندن یک شیء تکرشکننده (Iterator) است. یک تکرشکننده شیئی است که مسئول تولید عناصر بعدی در یک دنباله است. هنگامی که یک شیء تکرشپذیر در یک حلقه for استفاده میشود، ابتدا متد __iter__() آن فراخوانی میشود تا یک تکرشکننده ایجاد شود. سپس، حلقه for به طور مکرر متد __next__() تکرشکننده را فراخوانی میکند تا عناصر بعدی را دریافت کند.
به طور خلاصه، __iter__() یک شیء تکرشکننده را برمیگرداند، در حالی که __next__() عنصر بعدی را تولید میکند.
نقش __next__()
متد __next__() مسئول بازگرداندن عنصر بعدی در یک دنباله است. این متد توسط حلقه for فراخوانی میشود تا عناصر بعدی را دریافت کند. هنگامی که هیچ عنصر دیگری برای بازگرداندن وجود نداشته باشد، متد __next__() باید یک استثنای StopIteration را ایجاد کند. این استثنا به حلقه for اطلاع میدهد که به انتهای دنباله رسیده است و باید خاتمه یابد.
پیادهسازی یک تکرشپذیر سفارشی
برای درک بهتر نحوه کارکرد __iter__() و __next__()، یک کلاس سفارشی ایجاد میکنیم که یک دنباله از اعداد را تولید میکند.
class MyNumbers:
def __init__(self, max_value):
self.current = 0
self.max_value = max_value
def __iter__(self):
return self
def __next__(self):
if self.current < self.max_value:
value = self.current
self.current += 1
return value
else:
raise StopIteration
در این مثال:
- کلاس
MyNumbersیک دنباله از اعداد را از 0 تاmax_valueتولید میکند. - متد
__iter__()خود شیءMyNumbersرا برمیگرداند. این بدان معناست که شیءMyNumbersهم تکرشپذیر و هم تکرشکننده است. - متد
__next__()عنصر بعدی را بازمیگرداند. اگرcurrentکمتر ازmax_valueباشد، مقدارcurrentرا بازمیگرداند وcurrentرا یک واحد افزایش میدهد. در غیر این صورت، یک استثنایStopIterationرا ایجاد میکند.
حالا میتوانیم از این کلاس سفارشی در یک حلقه for استفاده کنیم:
numbers = MyNumbers(5)
for number in numbers:
print(number)
خروجی این کد به شرح زیر خواهد بود:
0 1 2 3 4
تکرشپذیرها و تکرشکنندهها: یک تمایز مهم
همانطور که در مثال بالا دیدیم، یک شیء میتواند هم تکرشپذیر و هم تکرشکننده باشد. اما این همیشه ضروری نیست. در واقع، یک تکرشپذیر باید یک تکرشکننده را برگرداند، اما خود تکرشپذیر لزوماً نباید یک تکرشکننده باشد.
به عنوان مثال، لیستها تکرشپذیر هستند، اما خود لیستها تکرشکننده نیستند. هنگامی که یک لیست در یک حلقه for استفاده میشود، متد __iter__() لیست یک شیء تکرشکننده جدید ایجاد میکند که بر روی عناصر لیست پیمایش میکند.
این تمایز مهم است زیرا به ما امکان میدهد تکرشپذیری را به روشی انعطافپذیر پیادهسازی کنیم. میتوانیم یک کلاس ایجاد کنیم که فقط تکرشپذیر باشد و یک کلاس جداگانه ایجاد کنیم که مسئول تولید عناصر بعدی باشد.
ایجاد یک تکرشکننده جداگانه
برای نشان دادن این موضوع، یک کلاس تکرشکننده جداگانه برای لیست ایجاد میکنیم:
class MyListIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
value = self.data[self.index]
self.index += 1
return value
else:
raise StopIteration
و سپس یک کلاس تکرشپذیر که از این تکرشکننده استفاده میکند:
class MyList:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyListIterator(self.data)
حالا میتوانیم از این کلاسها به صورت زیر استفاده کنیم:
my_list = MyList([10, 20, 30])
for item in my_list:
print(item)
خروجی این کد به شرح زیر خواهد بود:
10 20 30
کاربردهای پیشرفته
__iter__() و __next__() کاربردهای پیشرفتهای نیز دارند. به عنوان مثال، میتوان از آنها برای ایجاد:
- تولیدکنندهها (Generators): تولیدکنندهها توابعی هستند که از کلمه کلیدی
yieldبرای بازگرداندن عناصر بعدی استفاده میکنند. تولیدکنندهها به طور خودکار متدهای__iter__()و__next__()را پیادهسازی میکنند. - دنبالههای بینهایت: میتوان از
__iter__()و__next__()برای ایجاد دنبالههایی که هرگز به پایان نمیرسند، مانند دنباله اعداد فیبوناچی. - پردازش دادههای بزرگ: با استفاده از تکرشپذیری، میتوان دادههای بزرگ را به صورت تدریجی پردازش کرد، بدون اینکه نیاز به بارگذاری کل دادهها در حافظه باشد.
نتیجهگیری
درک متدهای __iter__() و __next__() برای تسلط بر مفهوم تکرشپذیری در پایتون ضروری است. با پیادهسازی این متدها، میتوان اشیاء سفارشی ایجاد کرد که میتوانند در حلقههای for و سایر ابزارهای تکرشپذیر استفاده شوند. این قابلیت به ما امکان میدهد کدهای انعطافپذیر و کارآمدتری بنویسیم و با دادهها به روشی مؤثرتر کار کنیم.

بدون دیدگاه