تکرارگرها در پایتون — به زبان ساده
در این مطلب، به مبحث تکرارگرها در پایتون پرداخته شده و مثالهایی برای درک بهتر مطلب، ارائه شده است.
تکرارگرها در پایتون
«تکرارگرها» (Iterators) اشیایی هستند که میتوانند (حول محور چیزی) تکرار شوند. در این راهنما، روش کار تکرارگرها در پایتون و چگونگی ساخت تکرارگر با استفاده از متدهای __iter__ و __next__ آموزش داده شده است. تکرارگرها در پایتون در همه جا حضور دارند.
آنها به صورت هوشمندانهای با استفاده از حلقه for، مولدها و comprehensions قابل پیادهسازی هستند؛ اما، از دید پنهان هستند. تکرارگر در پایتون یک شی است که میتواند (حول محور چیزی) تکرار شود؛ یک شی که داده (یک عنصر در هر زمان) باز میگرداند.
به بیان فنی، شی تکرارشونده پایتون باید دو متد خاص ()__iter__ و ()__next__ را پیادهسازی کند که مجموعه «پروتکل تکرارگر» (Iterator Protocol) نامیده میشوند. یک شی در صورتی تکرار شونده خوانده میشود که بتوان یک تکرارگر از آن گرفت. بیشتر ظرفهای توکار در پایتون مانند لیست، تاپل، رشته و دیگر موارد، تکرار شونده هستند. تابع ()iter (که متد ()__iter__ را فراخوانی میکند) یک تکرارگر را از آنها باز میگرداند.
تکرار کردن با استفاده از تکرارگرها در پایتون
از تابع ()next میتوان برای دستکاری دستی همه عناصر تکرارگر استفاده کرد. هنگامی که به پایان رسیده میشود و هیچ داده بیشتری برای بازگرداندن وجود ندارد، StopIteration اتفاق میافتد.
در ادامه، مثالی در همین رابطه آمده است.
1# define a list
2my_list = [4, 7, 0, 3]
3
4# get an iterator using iter()
5my_iter = iter(my_list)
6
7## iterate through it using next()
8
9#prints 4
10print(next(my_iter))
11
12#prints 7
13print(next(my_iter))
14
15## next(obj) is same as obj.__next__()
16
17#prints 0
18print(my_iter.__next__())
19
20#prints 3
21print(my_iter.__next__())
22
23## This will raise error, no items left
24next(my_iter)
راهکار هوشمندانهتری برای تکرار خودکار با استفاده از حلقه for وجود دارد. با استفاده از این راهکار، میتوان در طول هر شیئی که میتواند یک تکرارگر را بازگرداند، تکرار کرد؛ برای مثال در لیست، رشته، فایل و دیگر موارد میتوان این کار را انجام داد.
1>>> for element in my_list:
2... print(element)
3...
44
57
60
73
عملکرد حلقه for برای تکرارگرها
همانطور که در مثال بالا مشهود است، حلقه for قادر به تکرار شدن خودکار در طول یک لیست است. در حقیقت، حلقه for میتواند در طول هر چیز قابل تکراری، تکرار شود.
در ادامه، نگاهی دقیقتر به چگونگی پیادهسازی حلقه for در پایتون خواهیم داشت.
1for element in iterable:
2 # do something with element
در واقع، پیادهسازی آنچه بیان شد به صورت زیر پیاده میشود.
1# create an iterator object from that iterable
2iter_obj = iter(iterable)
3
4# infinite loop
5while True:
6 try:
7 # get the next item
8 element = next(iter_obj)
9 # do something with element
10 except StopIteration:
11 # if StopIteration is raised, break from loop
12 break
حلقه for میتواند یک شی تکرارگر iter_obj را با فراخوانی ()iter روی تکرار شونده بسازد. این حلقه for در واقع یک حلقه while بینهایت است. درون loop، برای دریافت عنصر بعدی، ()next فراخوانی و اجرا میشود و بدنه حلقه for بار ارزش پیادهسازی میشود. پس از آنکه همه اقلام اجرا شدند، StopIteration نمایش داده میشود که به صورت داخلی گرفتار شده و حلقه به پایان میرسد.
ساخت تکرارگر در پایتون
ساخت یک تکرارگر از پایه و در زبان پایتون بسیار آسان است. در این راستا، تنها نیاز است که متدهای ()__iter__ و ()__next__ پیادهسازی شوند. متد ()__iter__ خود شی تکرارگر را باز میگرداند. در صورت نیاز، برخی از مقداردهیهای اولیه نیز انجام میشود. متد ()__next__ باید آیتم بعدی در توالی را بازگرداند.
در رسیدن به پایان و در فراخوانی زیررشتهها، StopIteration به وقوع میپیوندد. در اینجا، مثالی نشان داده شده است که توان ۲ اعداد را در هر تکرار به دست میدهد. توان از صفر شروع میشود و تا عددی که کاربر تنظیم کرده ادامه خواهد داشت.
class PowTwo: """Class to implement an iterator of powers of two""" def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n <= self.max: result = 2 ** self.n self.n += 1 return result else: raise StopIteration
همچنین، میتوان از حلقه for برای تکرار کردن در کلاس تکرارگر استفاده کرد.
>>> for i in PowTwo(5): ... print(i) ... 1 2 4 8 16 32
تکرارگرهای بینهایت پایتون
الزامی نیست که آیتمی از یک تکرارگر خارج شود. حلقههای بینهایتی میتوانند وجود داشته باشند که هیچگاه تمام نمیشوند. البته، باید در مدیریت چنین تکرارگرهایی دقت به خرج داد. در ادامه، مثال سادهای برای نمایش یک تکرارگر نامتناهی ارائه شده است.
تابع توکار ()iter را میتوان با دو آرگومان فراخوانی کرد که در آن اولین آرگومان باید یک شی قابل فراخوانی (تابع) و دومیین آرگومان یک sentinel باشد. تکرارگر، این تابع را تا هنگامی که به مقدار محافظ (sentinel | منظور همان مقدار پایان حلقه است) برسد، فراخوانی میکند.
>>> int() 0 >>> inf = iter(int,1) >>> next(inf) 0 >>> next(inf) 0
میتوان مشاهده کرد که تابع ()int همیشه مقدار ۰ را باز میگرداند. بنابراین، پاس دادن آن به عنوان (iter(int,1، موجب میشود که یک تکرارگر بازگردانده شود که ()int را تا بازگرداندن مقدار برابر با ۱ فراخوانی میکند. این اتفاق هرگز نمیافتد و بنابراین، برنامه در یک حلقه بیپایان گیر میکند. کاربران، میتوانند حلقههای بی پایان متنوعی را بسازند. تکرارگر زیر، به لحاظ تئوری همه اعداد فرد را باز میگرداند.
1class InfIter:
2 """Infinite iterator to return all
3 odd numbers"""
4
5 def __iter__(self):
6 self.num = 1
7 return self
8
9 def __next__(self):
10 num = self.num
11 self.num += 2
12 return num
یک نمونه به صورت زیر اجرا میشود:
>>> a = iter(InfIter()) >>> next(a) 1 >>> next(a) 3 >>> next(a) 5 >>> next(a) 7
باید توجه داشت که در چنین حلقههایی، یک شرط توقف حلقه نیز قرار داده شود. مزیت استفاده از این نوع تکرارگرها در آن است که منابع را حفظ میکنند.
همانطور که پیشتر نشان داده شد، میتوان اعداد فرد را بدون ذخیره کل سیستم عددی، در حافظه ذخیره کرد. می توان آیتمهای نامتناهی (به لحاظ تئوری) در یک حافظه متناهی داشت. همچنین، تکرارگرها میتوانند موجب جالبتر به نظر رسیدن کدها شوند.