Iterator

Iterator (پیمایشگر) یک شیء است که دارای تعدادی مقادیر قابل شمارش می‌باشد. Iterator ها را می‌توان در بخش‌های مختلفی از پایتون یافت. این اشیاء درون حلقه‌های for و … مورد استفاده قرار می‌گیرند. به زبان ساده Iterator ها در پایتون اشیایی هستند که می‌توان بر روی آنها عملی را تکرار کرد. این اشیاء هر بار یک عنصر را بر می‌گردانند.

در عمل یک شیء پیمایشگر (Iterator) باید دو تابع خاص ()__iter__ و ()__next__ را پیاده سازی کند. به اشیایی که بتوان از آنها پیمایشگر دریافت نمود، Iterable گفته می‌شود.List ،tuple ،Dictionary و Set ها همگی Iterable هستند. همه این اشیاء یک تابع خاص به نام ()__iter__ دارند که با ارسال آنها به تابع ()iter فراخوانی شده و یک پیمایشگر در اختیار شما قرار می‌دهد.

با استفاده از تابع ()next می‌توان همه عناصر پیمایشگر را، پیمایش نمود. زمانی که به انتهای عناصر رسیدیم و داده دیگری برای بازگشت وجود نداشت، خطای StopIteration رخ خواهد داد. در ادامه مثالی از این تابع را بررسی می‌کنیم:

tuple = ("apple", "banana", "cherry")
myIterator = iter(tuple)

print(next(myIterator))
print(next(myIterator))
print(next(myIterator))
apple
banana
cherry

همانطور که در کد بالا مشاهده می‌کنید، تابع ()iter یک مجموعه مانند tuple دریافت کرده و سپس یک شیء پیمایشگر را بر می‌گرداند. حال می‌توان با فراخوانی تابع ()next در میان عناصر این پیمایشگر گشته و تک تک عناصر را به دست آورید. کد بالا به صورت خودکار توسط پایتون به صورت زیر اجرا می‌شود:

tuple = ("apple", "banana", "cherry")
myIterator = tuple.__iter__()

print(myIterator.__next__())
print(myIterator.__next__())
print(myIterator.__next__())
apple
banana
cherry

در پایتون رشته‌ها هم که یک توالی از کاراکترها هستند نیز اشیایی قابل پیمایش می‌باشند. به کد زیر توجه کنید:

string = "banana"
myItreator = iter(string)

print(next(myItreator))
print(next(myItreator))
print(next(myItreator))
print(next(myItreator))
print(next(myItreator))
print(next(myItreator))
b
a
n
a
n
a

روش مناسب‌تر برای پیمایش خودکار بر روی عناصر استفاده از حلقه for است. این حلقه بر روی اشیایی که می‌توانند پیمایشگر (iterator) بر گردانند از قبیل List ها و رشته‌ها، قابل اعمال است. در واقع حلقه for قادر به تکرار بر روی هر شی iterable است. مثلاً همین کد بالا را به صورت زیر هم می‌توان نوشت:

string = "banana"

for char in string:
    print(char)
b
a
n
a
n
a

بهتر است نگاهی به نحوه پیاده سازی حلقه for در پایتون بیاندازیم:

for element in iterable:
    # do something with element

این تکه کد، در واقع به صورت زیر پیاده سازی می‌شود:

iterator = iter(iterable)

while True:
    try:
        element = next(iterator)
    except StopIteration:
        break

ایجاد پیمایشگر

ایجاد یک پیمایشگر جدید در پایتون کار ساده‌ای است. تنها نیاز است یک کلاس ایجاد کرده و دو تابع ()__iter__ و ()__next__را پیاده سازی کنید. تابع ()__iter__ شیء پیمایشگر (iterator) را بر می‌گرداند. اگر نیاز به مقداردهی‌های اولیه باشد، در این تابع صورت می‌گیرد. تابع ()__next__ باید عنصر بعدی در دنباله مربوطه را بر گرداند. پس از رسیدن به پایان دنباله، در فراخوانی بعدی باید استثناء StopIteration را مدیریت کند. به مثال زیر توجه کنید:

  1: class MyNumbers:
  2:   def __iter__(self):
  3:     self.a = 1
  4:     return self
  5: 
  6:   def __next__(self):
  7:     x = self.a
  8:     self.a += 1
  9:     return x
 10: 
 11: myclass = MyNumbers()
 12: myItreator = iter(myclass)
 13: 
 14: print(next(myItreator))
 15: print(next(myItreator))
 16: print(next(myItreator))
 17: print(next(myItreator))
 18: print(next(myItreator))
1
2
3
4
5

در کد بالا و در خطوط 4-2 تابع ()__iter__ را پیاده سازی کرده‌ایم در این تابع گفته‌ایم که کلاس دارای یک فیلد به نام a بوده و مقدار آن 1 است. سپس در خطوط 9-6 گفته‌ایم که با هر بار فراخوانی تابع ()__next__ فیلد بعدی که یک واحد بزرگتر از a است برگشت داده شود. در خط 11 یک نمونه از کلاس را ایجاد کرده و در خط 12 آن را به تابع ()iter ارسال می‌کنیم تا یک شیء پیمایشگر در اختیار ما قرار دهد. با هر بار فراخوانی تابع ()next در خطوط 18-14 یک عدد به ما برگردانده می‌شود. حال فرض کنید که می‌خواهید Iterator ی که ایجاد کرده‌اید اعداد را به توان 2 رسانده و برگشت دهد. برای این منظور می‌توان خط 9 کد بالا را به صورت زیر تغییر داد:

return x ** 2

می‌توان نتیجه گرفت که شما می‌توانید پیمایشگر های کاملاً اختصاصی برای مقاصد خاص ایجاد کنید. استفاده از پیمایشگرها باعث می‌شود، کد با ساختار بهتری داشته باشید. روش آسان‌تری برای ساخت پیمایشگر (iterator) وجود دارد. استفاده از مولد (Generator) و yield که در مطلب بعدی بررسی می‌شوند.