জেনারেটর

এই চ্যাপ্টারটি সর্বশেষ হালনাগাদ হয়েছেঃ Tue Nov 28 2017 03:11:13 GMT+0000 (UTC) সময়ে

আগের চ্যাপ্টার গুলোতে iterable নিয়ে বেশ কিছু কথা বলা হয়েছে। লিস্ট, টাপল এসব হচ্ছে একধরনের iterable. জেনারেটরও এক রকমের iterable. কিন্তু লিস্ট এর মত এর এলিমেন্ট গুলোকে ইন্ডেক্সিং করা যায় না। কিন্তু তার মানে এই না যে, এর এলিমেন্ট গুলোকে অ্যাক্সেস করা যায় না। বরং for লুপ দিয়ে এর এলিমেন্ট গুলোকেও অ্যাক্সেস করা যায়। সব চেয়ে বড় কথা এলিমেন্ট এর চেইন তৈরি এবং অ্যাক্সেস এক সাথেই করা যায়।

সাধারণ ফাংশন এবং yield স্টেটমেন্ট ব্যবহার করেই এই বিশেষ ধরনের iterable কে তৈরি করা যায়। নিচের উদাহরণ দেখে নেই -

>>> def my_iterable():
...     i = 5
...     while i > 0:
...         yield i
...         i -= 1
...
>>> for i in my_iterable():
...     print(i)
...
5
4
3
2
1

এখানে my_iterable() দেখতে একটা সাধারণ ফাংশন। কিন্তু একটু একটু খেয়াল করলে দেখা যাবে, এখানে রিটার্ন এর বদলে yield কিওয়ার্ড ব্যবহার করা হয়েছে। এই ফাংশন খুব সহজ ভাবে while লুপ ব্যবহার করে 5 থেকে 1 পর্যন্ত রিটার্ন (yield) করে। কার কাছে রিটার্ন করে? ওই ফাংশনের নিচেই আমাদের তৈরি একটা for লুপ ওয়ালা স্টেটমেন্টের কাছে। এবং সেই লুপের মধ্যে print ব্যবহার করে এর কাছে আসা রিটার্ন ভ্যালুকে বার বার প্রিন্টও করা যাচ্ছে।

এক্ষেত্রে বলাই যায় যে, লিস্ট এর মত আমাদের my_iterable() -ও একটা iterable যাকে for লুপ দিয়ে অ্যাক্সেস করে কিছু ভ্যালু পাওয়া যায় যে ভ্যালু গুলো কিনা একটু আগেই আমাদের মত করেই তৈরি।

গুরুত্বপূর্ণ একটা ব্যপার খেয়াল করুন, সাধারণ কোন ফাংশনকে বার বার কল করলে সেই ফাংশন বার বার নতুন ভাবে এক্সিকিউট হয় এবং কাজ শেষে নতুন ভ্যালু রিটার্ন করে। কিন্তু এই ক্ষেত্রে একটা মজার ব্যপার ঘটছে। তা হল - যদিও for লুপ দিয়ে বার বার my_iterable() ফাংশনকে কল করা হচ্ছে কিন্তু ওই ফাংশনের মধ্যে থাকা i এর ভ্যালু কিন্তু ঠিকি সেইভ থাকছে (স্মরণ রাখছে) অর্থাৎ while লুপ টি প্রথমে i এর মান 5 তারপর 4 এভাবে রিটার্ন করছে। এমন না যে, প্রত্যেক বার 5 রিটার্ন হচ্ছে যেভাবে একটা সাধারণ ফাংশনকে একাধিক বার কল করলে হত।

আরেকটা উধাহরন -

>>> def even_numbers(x):
...     for i in range(x):
...         if i % 2 == 0:
...             yield i
...
>>> even_nums_list = list(even_numbers(10))
>>> print(even_nums_list)
[0, 2, 4, 6, 8]

আবার আসি সেই বিশেষ ধরনের একটা ফাংশন যা একাধারে কিছু ভ্যালু yield করে পক্ষান্তরে একে একটি iterable হিসেবে প্রকাশ করে। এখানে even_numbers() একটি ফাংশন তথা জেনারেটর (কারণ yield ব্যবহার করছে) যা একটি নির্দিষ্ট রেঞ্জ পর্যন্ত কিছু ভ্যালুর উপর for লুপ দিয়ে অপারেশন চালিয়ে সেখান থেকে শুধু মাত্র জোড় সংখ্যা গুলোকে yield (রিটার্ন) করে। ততক্ষণ পর্যন্ত রিটার্ন করে যতক্ষণ তার কাজের সীমা অর্থাৎ তার কাছে আর্গুমেন্ট হিসেবে আসা ভ্যালুর উপর for লুপ এর অপারেশনের শেষ পর্যন্ত।

ওদিকে খুব সহজেই আমরা ওই জেনারেটর কর্তৃক রিটার্ন করা ভ্যালু গুলোকে list() ফাংশনের মধ্যে দিয়ে সেখান থেকে একটি লিস্ট পেতে পারি যা শেষ লাইনে প্রিন্ট করে দেখানো হয়েছে।