Skip to main content

পাইথনে জেনারেটর ও তার খুঁটিনাটি বিষয়

এখানে আমরা দেখব যে পাইথনে জেনারেটর বিষয়টা কি । আমরা কিভাবে জেনারেটর বানাতে পারি । জেনারেটর এক্সপ্রেশন কি ? আমাদের কেন ও কি অবস্থায় জেনারেটর ব্যবহার করা দরকার । তো শুরু করা যাক ।

জেনারেটর কি ?

পাইথনে iterator তৈরি করার সবচেয়ে সহজ উপয় হল জেনারেটর । সাধারণ ভাবে বলতে গেলে জেনারেটর একটা ফাংশন যেটা একটা iterator অবজেক্ট রিটার্ন করে । যেটাকে পরে আমরা iterate করতে পারি । এবং যেটা কেবল মাত্র একবারই iterate করা যাবে ।

এখন প্রশ্ন হল এই জেনারেটর কিভাবে বানানো যায় ?

জেনারেটর বানানো খুব সহজ । আমরা সাধারণ ফাংশন যেভাবে লিখি ঠিক সেভাবেই আমরা জেনারেটর বানাতে পারি । কিন্তু এখানে return স্টেটমেন্টের পরিবর্তে yield স্টেটমেন্ট থাকবে । যদি কোন একটা ফাংশনে অন্তত একটা yield স্টেটমেন্ট থাকে তবে সেটা কে আমরা জেনারেটর বলতে পারি । তবে একটা জেনারেটরে একাধিক yield স্টেটমেন্ট থাকতে পারে ।

yield এবং return দুইটাই ফাংশন থেকে কোন ভেলু রিটার্ন করে কিন্তু দুইটার মধ্যে পার্থক্য হল return স্টেটমেন্ট একটা ফাংশন কে পুরাপুরি terminate করে ফেলে । কিন্তু yield ফাংশন কে terminate না করে ফাংশনকে pauses করে রাখে + ফাংশনের বর্তমান অবস্থাও সেভ করে রাখে এবং পরবর্তীতে ফাংশনের আগের অবস্থা থেকেই কাজ শুরু করে ।

কি জটিল মনে হচ্ছে ? একটা কোড দেখি তাহলে বিষয়টা সহজ হয়ে যাবে । মনে করি আমাদের my_generator() নামে একটা ফাংশন আছে যেটার মধ্যে একাধিক yield স্টেটমেন্ট আছে ।
# a basic generator function
def my_generator():
n = 1
print('This is printed first and pauses until we call the next()')
# Generator function contains yield statements
yield n
n += 1
print('This is printed second and pauses until we call the next()')
yield n
n += 1
print('This is printed at last')
yield n
# it return an iterator object
a = my_generator()
# we need to use next() to iterate item
next(a)
# This is printed first and pauses until we call the next()
output:
1
view raw generator-1.py hosted with ❤ by GitHub

next(a) কল করার পরে দেখা যাচ্ছে যে আমাদের পুরা ফাংশন কিন্তু কল হয় নি । 6 নং লাইনে এসে থেমে আছে । 6 নং লাইনের আগেই কাজ কিন্তু সে ঠিকই করে ফেলছে । এখন আমরা যদি আবার next(a) দিয়ে কল করি তবে কি ঘটনা ঘটে দেখি ।
# যখন ফাংশন n কে yields or return করে দিবে, তখন ফাংশনটি paused(থেমে) হয়ে থাকবে এবং কন্ট্রোলটা চলে যাবে কলার ফাংশনের কাছে ।
# প্রত্যেক next() কল এ লোকাক ভেরিয়েবল এবং সেটার পরিবর্তন কে মনে রাখবে ।
# This is printed second and pauses until we call the next()
next(a)
output:
2
view raw generator-2.py hosted with ❤ by GitHub

এখানে দেখা যাচ্ছে জা ৮ নং লাইন থেকে ১০ নং লাইন পর্যন্ত কাজ হয়েছে । এবং n পরিবর্তিত মান কে সে মনে রেখেছে । ফলে n = 1 এর সাথে 1 যোগ করে নতুন মান ২ রিটার্ন করেছে । এখন আমরা যদি আবার next(a) কল করি তবে সে একই কাজ করবে ।
next(a)
# This is printed at last and pauses until we call the next()
output:
3
view raw generator-2.py hosted with ❤ by GitHub

সে কিন্তু n = 2 এর সাথে ১ যোগ করে ৩ রিটার্ন করেছে । আচ্ছা এখন আমরা যদি আবার next(a) কে কল করি তবে কি ঘটনা ঘটবে ? দেখা যাক রান করে দেখি কি ঘটে ।
next(a)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-17-3f6e2eea332d> in <module>()
----> 1 next(a)
StopIteration:
view raw generator-4.py hosted with ❤ by GitHub

Traceback এ দেখে যাচ্ছে যে StopIteration নামে একটা exception খাইছে । এর কারণ আমারা যে generator বানিয়েছি সেটা তো ৩ টা জিনিসই রিটার্ন করে । তো সেখান থেকে আমি কখনই ৪ টা জিনিস আশা করতে পারি না ।
উপরের ঘটনাগুলির সারাংশ এইরকম হতে পারে
  1. প্রত্যেক কল এ ভেরিয়েবল n এর মান মনে রাখা হয় ।
  2. সাধারণ ফাংশনের মত ভেলু রিটার্ন করার পরে লোকাল ভেরিয়েবল ধ্বংস হয়ে যায় না ।
  3. Generator অবজেক্ট কেবল একবারই iterate করা যায়
  4.  নতুন করে iterate করা জন্য আমাদের কে a = my_generator() আবার কল করতে হবে ।

Python Generators কে লুপের মাধ্যমে iterate করা :

# make a iterator object
my_iterator_object = my_generator()
for item in my_iterator_object:
print("value of n is:", item)
output:
This is printed first and pauses until we call the next()
value of n is: 1
This is printed second and pauses until we call the next()
value of n is: 2
This is printed at last and pauses until we call the next()
value of n is: 3
view raw generator-5.py hosted with ❤ by GitHub

Python Generator Expression:

এটা হল ফাংশন ক্রিয়েট না করেই জেনারেটর অবজেক্ট ক্রিয়েট করা । আমাদের জেনারেটর ফাংশন যদি খুব ছোট হয় তবে সেটা ইমপ্লিমেন্ট করা জন্য আলাদা করে ফাংশন ডিফাইন করার দরকার নাই । ল্যামডা ফাংশন যেমন anonymous function ক্রিয়েট করতে পারে । ঠিক তেমনি ভাবে generator expression, anonymous generator ফাংশন ক্রিয়েট করতে পারে ।

Generator expression এর সিনট্যাক্স list comprehension এর মতই শুধু পার্থক্য হল এখানে square brackts এর পরিবর্তে round parentheses ব্যবহার করা হয় । কিছু উদাহরণ দেখা যাক ।
# initilize a list
my_list = [1, 3, 5, 8, 11, 21]
# square each element of our list using list comprehension
square_of_my_list = [x**2 for x in my_list]
# print list
print("list:", square_of_my_list)
# একই কাজ generator expression এর মাধ্যমেও করা যায়
my_generator_object = (x**2 for x in my_list)
print ("object type is:", type(my_generator_object))
print ("generator list is: ")
# iterate item
for item in my_generator_object:
print (item)
output:
list: [1, 9, 25, 64, 121, 441]
object type is: <class 'generator'>
generator list is:
1
9
25
64
121
441
view raw generator-6.py hosted with ❤ by GitHub

কেন আমরা Generator ব্যবহার করব ?

অনেকের মনে হতে পারে যে জেনারেটরের কাজ তো সাধারণ ফাংশন ও লিস্ট কম্প্রিহেন্সনের মাধ্যমেই করা যায় তবে এটা দরকার টা কি ? এর বেশ কিছু সুবিধা আছে তার মধ্যে অন্যতম

মেমরি খরচ অনেক কমায়ে দেয় :

প্রশ্ন হতে পারে যে মেমরি খরচ কিভাবে কমায়ে দেয় ? মনে করেন আমাদের একটা ফাংশন আছে যেটা 2GB ইমেজ ফাইল রিড করে একটা লিস্ট ভেরিয়েবলে স্টোর করে রাখে সব ফাইল রিড করা শেষ হলে লিস্ট টি রিটার্ন করে দেয় । আমাদের ফাংশন টা যদি সাধারণ ফাংশন হয় তবে এটা পুরা 2GB ইমেজের ডাটা কে মেমরিতে তে লোড করে রাখবে যতক্ষণ না সেটা ফাইনাল লিস্ট কে return করে ।

কিন্তু আমরা যদি জেনারেটর ব্যবহার করি তবে আমাদের কে পুরা 2GB ইমাজের ডাটা কে মেমরিতে লোড করতে হচ্ছে না । কারণ আমরা দেখেছি যে জেনারেটর একক সময়ে কেবল একটাই আইটেম রিটার্ন করে ।

অসীম একটা Stream কে রিপ্রেজেন্ট করা যায়:

আপনার যদি অসীম সংখ্যক ডাটা থাকে তবে আপনি খুব সহজেই জেনারেটর দিয়ে সেটা কে রিপ্রেজেন্ট করতে পারবেন । কি বিশ্বাস হচ্ছে না ? আমরও অবশ্য শুরুতে বিশ্বাস হয় নি । কিন্তু এখন হয় । একটা উদাহরণ দেই তাহলেই বিশ্বাস হয়ে যাবে ।

Fibonacci numbers এর সাথে আমরা অনেকেই পরিচিত । তো আমরা Fibonacci number এর অসীম stream generate করার চেষ্টা করি ।
def infinity_fibonacci_numbers():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# import sys
# print (sys.maxsize)
import itertools
list(itertools.islice(infinity_fibonacci_numbers(), 10000, 10002))
Output:
[33644764876431783266621612005107543310302148460680063906564769974680081442166662368155595513633734025582065332680836159373734790483865268263040892463056431887354544369559827491606602099884183933864652731300088830269235673613135117579297437854413752130520504347701602264758318906527890855154366159582987279682987510631200575428783453215515103870818298969791613127856265033195487140214287532698187962046936097879900350962302291026368131493195275630227837628441540360584402572114334961180023091208287046088923962328835461505776583271252546093591128203925285393434620904245248929403901706233888991085841065183173360437470737908552631764325733993712871937587746897479926305837065742830161637408969178426378624212835258112820516370298089332099905707920064367426202389783111470054074998459250360633560933883831923386783056136435351892133279732908133732642652633989763922723407882928177953580570993691049175470808931841056146322338217465637321248226383092103297701648054726243842374862411453093812206564914032751086643394517512161526545361333111314042436854805106765843493523836959653428071768775328348234345557366719731392746273629108210679280784718035329131176778924659089938635459327894523777674406192240337638674004021330343297496902028328145933418826817683893072003634795623117103101291953169794607632737589253530772552375943788434504067715555779056450443016640119462580972216729758615026968443146952034614932291105970676243268515992834709891284706740862008587135016260312071903172086094081298321581077282076353186624611278245537208532365305775956430072517744315051539600905168603220349163222640885248852433158051534849622434848299380905070483482449327453732624567755879089187190803662058009594743150052402532709746995318770724376825907419939632265984147498193609285223945039707165443156421328157688908058783183404917434556270520223564846495196112460268313970975069382648706613264507665074611512677522748621598642530711298441182622661057163515069260029861704945425047491378115154139941550671256271197133252763631939606902895650288268608362241082050562430701794976171121233066073310059947366875,
54438373113565281338734260993750380135389184554695967026247715841208582865622349017083051547938960541173822675978026317384359584751116241439174702642959169925586334117906063048089793531476108466259072759367899150677960088306597966641965824937721800381441158841042480997984696487375337180028163763317781927941101369262750979509800713596718023814710669912644214775254478587674568963808002962265133111359929762726679441400101575800043510777465935805362502461707918059226414679005690752321895868142367849593880756423483754386342639635970733756260098962462668746112041739819404875062443709868654315626847186195620146126642232711815040367018825205314845875817193533529827837800351902529239517836689467661917953884712441028463935449484614450778762529520961887597272889220768537396475869543159172434537193611263743926337313005896167248051737986306368115003088396749587102619524631352447499505204198305187168321623283859794627245919771454628218399695789223798912199431775469705216131081096559950638297261253848242007897109054754028438149611930465061866170122983288964352733750792786069444761853525144421077928045979904561298129423809156055033032338919609162236698759922782923191896688017718575555520994653320128446502371153715141749290913104897203455577507196645425232862022019506091483585223882711016708433051169942115775151255510251655931888164048344129557038825477521111577395780115868397072602565614824956460538700280331311861485399805397031555727529693399586079850381581446276433858828529535803424850845426446471681531001533180479567436396815653326152509571127480411928196022148849148284389124178520174507305538928717857923509417743383331506898239354421988805429332440371194867215543576548565499134519271098919802665184564927827827212957649240235507595558205647569365394873317659000206373126570643509709482649710038733517477713403319028105575667931789470024118803094604034362953471997461392274791549730356412633074230824051999996101549784667340458326852960388301120765629245998136251652347093963049734046445106365304163630823669242257761468288461791843224793434406079917883360676846711185597501]
view raw generator-7.py hosted with ❤ by GitHub

বিশাল বড় দুইটা Fibonacci সংখ্যা দিয়ে দিছে । কি ভয়ঙ্কর সুন্দর ও সাঙ্ঘাতিক ব্যাপার । আপনার মেশিন যতটুকু লোড নিতে পারবে সেটা উপর ভিত্তি করে আপনি 10000, 10002 এইটা দুইটা মান ছোট ও বড় বসাতে পারেন । আমার মেশিনে এর থেকে বড় সংখ্যা দিলে মেশিনের অবস্থা খারাপ হয়ে যাবে ।

আপনার সিস্টেম সবচেয়ে বড় কি সংখ্যা দেয়া যেতে পারে তার জন্য আপনই sys.maxsize দিয়ে সাইজ দেখে নিতে পারেন ।

এই দুইটা বাদেও generator এর আরও কিছু আপ্লিকেশন আছে । সেগুলো জানার জন্য আপনি গুগল করতে পারেন । আজকে এ পর্যন্তই ।

মুল লেখা: https://github.com/menon92/Python-Learning/blob/master/python_generator_by_example.ipynb

Comments

Popular posts from this blog

উবুন্টুতে রুট পাসওয়ার্ড ভুলে তা রিকভার করার উপায় ।

যদি কেউ রুট পাসওয়ার্ড ভুলে যান তাহলে নিচের কাজ গুলো করে নতুন পাসওয়ার্ড সেট করতে পারবেন: প্রথমে পিসি রিস্টার্ট দিন । দিয়ে UP/DOWN করে kernel version সিলেক্ট করে e চাপুন । ব্ল্যাক Screen আসবে এবার একটা Space দিয়ে লিখুন “Single” [ Enter ] এরপর b চাপুন ফলাফল : লিনাক্সের Single user Mood এ চলে আসছেন । এখন লিখুন passwd root [ Enter ] এখন নতুন পাসওয়ার্ড খানা টাইপ করেন [ এন্টার ] আবার টাইপ করেন [ এন্টার ] কাজ শেষ , এবার reboot টাইপ করেন । এখন নতুন পাসওয়ার্ড দিয়ে লগইন করেন। পুনশ্চ : যদি আপনার উইন্ডোজ এর সাথে ডুয়েল বুট করা থাকে তবে এই প্রক্রিয়া কাজ করবে না ।

Fix The BIOS in this system is not fully ACPI compliant in Windows 7

এই সমস্যা সমাধান করার জন্য আপনি নিচের ধাপ গুলো অনুসরণ করুন। ধাপ ১ :  আপানর  কম্পিউটার এ উইন্ডোজ এর ডিস্ক থেকে বুট করুন । নীচের মত উইন্ডো আসলে Shift + F10 চাপুন । এর ফলে কমান্ড প্রম্প্ট ওপেন হবে। ধাপ ২ : এখন CMD তে নিচের কমান্ড গুলি ধারবাহিক ভাবে লিখুন C: bootrec /FixMbr bootrec /FixBoot bootrec /RebuildBcd exit  এখানে  C হলো যে ড্রাইভ এ উইন্ডোজ দেয়া আছে।  আপনার যদি অন্য কোনো ড্রাইভ ( D, E, F, ..... ) এ উইন্ডোজ দেয়া থাকে তবে আপনাকে C এর জায়গায় সেই ড্রাইভ এর নাম লিখতে হবে।  উপরের সব কমান্ড যদি সঠিক ভাবে বিল্ড হয় তবে আপনি আপনার কম্পিউটার রিস্টার্ট দিন।  দেখবেন আপনার সমস্যা সমাধান হয়ে গেছে :D