এখানে আমরা দেখব যে পাইথনে জেনারেটর বিষয়টা কি । আমরা কিভাবে জেনারেটর বানাতে পারি । জেনারেটর এক্সপ্রেশন কি ? আমাদের কেন ও কি অবস্থায় জেনারেটর ব্যবহার করা দরকার । তো শুরু করা যাক ।
yield এবং return দুইটাই ফাংশন থেকে কোন ভেলু রিটার্ন করে কিন্তু দুইটার মধ্যে পার্থক্য হল return স্টেটমেন্ট একটা ফাংশন কে পুরাপুরি terminate করে ফেলে । কিন্তু yield ফাংশন কে terminate না করে ফাংশনকে pauses করে রাখে + ফাংশনের বর্তমান অবস্থাও সেভ করে রাখে এবং পরবর্তীতে ফাংশনের আগের অবস্থা থেকেই কাজ শুরু করে ।
কি জটিল মনে হচ্ছে ? একটা কোড দেখি তাহলে বিষয়টা সহজ হয়ে যাবে । মনে করি আমাদের my_generator() নামে একটা ফাংশন আছে যেটার মধ্যে একাধিক yield স্টেটমেন্ট আছে ।
next(a) কল করার পরে দেখা যাচ্ছে যে আমাদের পুরা ফাংশন কিন্তু কল হয় নি । 6 নং লাইনে এসে থেমে আছে । 6 নং লাইনের আগেই কাজ কিন্তু সে ঠিকই করে ফেলছে । এখন আমরা যদি আবার next(a) দিয়ে কল করি তবে কি ঘটনা ঘটে দেখি ।
এখানে দেখা যাচ্ছে জা ৮ নং লাইন থেকে ১০ নং লাইন পর্যন্ত কাজ হয়েছে । এবং n পরিবর্তিত মান কে সে মনে রেখেছে । ফলে n = 1 এর সাথে 1 যোগ করে নতুন মান ২ রিটার্ন করেছে । এখন আমরা যদি আবার next(a) কল করি তবে সে একই কাজ করবে ।
সে কিন্তু n = 2 এর সাথে ১ যোগ করে ৩ রিটার্ন করেছে । আচ্ছা এখন আমরা যদি আবার next(a) কে কল করি তবে কি ঘটনা ঘটবে ? দেখা যাক রান করে দেখি কি ঘটে ।
Traceback এ দেখে যাচ্ছে যে StopIteration নামে একটা exception খাইছে । এর কারণ আমারা যে generator বানিয়েছি সেটা তো ৩ টা জিনিসই রিটার্ন করে । তো সেখান থেকে আমি কখনই ৪ টা জিনিস আশা করতে পারি না ।
উপরের ঘটনাগুলির সারাংশ এইরকম হতে পারে
Generator expression এর সিনট্যাক্স list comprehension এর মতই শুধু পার্থক্য হল এখানে square brackts এর পরিবর্তে round parentheses ব্যবহার করা হয় । কিছু উদাহরণ দেখা যাক ।
কিন্তু আমরা যদি জেনারেটর ব্যবহার করি তবে আমাদের কে পুরা 2GB ইমাজের ডাটা কে মেমরিতে লোড করতে হচ্ছে না । কারণ আমরা দেখেছি যে জেনারেটর একক সময়ে কেবল একটাই আইটেম রিটার্ন করে ।
Fibonacci numbers এর সাথে আমরা অনেকেই পরিচিত । তো আমরা Fibonacci number এর অসীম stream generate করার চেষ্টা করি ।
বিশাল বড় দুইটা Fibonacci সংখ্যা দিয়ে দিছে । কি ভয়ঙ্কর সুন্দর ও সাঙ্ঘাতিক ব্যাপার । আপনার মেশিন যতটুকু লোড নিতে পারবে সেটা উপর ভিত্তি করে আপনি 10000, 10002 এইটা দুইটা মান ছোট ও বড় বসাতে পারেন । আমার মেশিনে এর থেকে বড় সংখ্যা দিলে মেশিনের অবস্থা খারাপ হয়ে যাবে ।
আপনার সিস্টেম সবচেয়ে বড় কি সংখ্যা দেয়া যেতে পারে তার জন্য আপনই sys.maxsize দিয়ে সাইজ দেখে নিতে পারেন ।
এই দুইটা বাদেও generator এর আরও কিছু আপ্লিকেশন আছে । সেগুলো জানার জন্য আপনি গুগল করতে পারেন । আজকে এ পর্যন্তই ।
মুল লেখা: https://github.com/menon92/Python-Learning/blob/master/python_generator_by_example.ipynb
জেনারেটর কি ?
পাইথনে iterator তৈরি করার সবচেয়ে সহজ উপয় হল জেনারেটর । সাধারণ ভাবে বলতে গেলে জেনারেটর একটা ফাংশন যেটা একটা iterator অবজেক্ট রিটার্ন করে । যেটাকে পরে আমরা iterate করতে পারি । এবং যেটা কেবল মাত্র একবারই iterate করা যাবে ।এখন প্রশ্ন হল এই জেনারেটর কিভাবে বানানো যায় ?
জেনারেটর বানানো খুব সহজ । আমরা সাধারণ ফাংশন যেভাবে লিখি ঠিক সেভাবেই আমরা জেনারেটর বানাতে পারি । কিন্তু এখানে return স্টেটমেন্টের পরিবর্তে yield স্টেটমেন্ট থাকবে । যদি কোন একটা ফাংশনে অন্তত একটা yield স্টেটমেন্ট থাকে তবে সেটা কে আমরা জেনারেটর বলতে পারি । তবে একটা জেনারেটরে একাধিক yield স্টেটমেন্ট থাকতে পারে ।yield এবং return দুইটাই ফাংশন থেকে কোন ভেলু রিটার্ন করে কিন্তু দুইটার মধ্যে পার্থক্য হল return স্টেটমেন্ট একটা ফাংশন কে পুরাপুরি terminate করে ফেলে । কিন্তু yield ফাংশন কে terminate না করে ফাংশনকে pauses করে রাখে + ফাংশনের বর্তমান অবস্থাও সেভ করে রাখে এবং পরবর্তীতে ফাংশনের আগের অবস্থা থেকেই কাজ শুরু করে ।
কি জটিল মনে হচ্ছে ? একটা কোড দেখি তাহলে বিষয়টা সহজ হয়ে যাবে । মনে করি আমাদের my_generator() নামে একটা ফাংশন আছে যেটার মধ্যে একাধিক yield স্টেটমেন্ট আছে ।
next(a) কল করার পরে দেখা যাচ্ছে যে আমাদের পুরা ফাংশন কিন্তু কল হয় নি । 6 নং লাইনে এসে থেমে আছে । 6 নং লাইনের আগেই কাজ কিন্তু সে ঠিকই করে ফেলছে । এখন আমরা যদি আবার next(a) দিয়ে কল করি তবে কি ঘটনা ঘটে দেখি ।
এখানে দেখা যাচ্ছে জা ৮ নং লাইন থেকে ১০ নং লাইন পর্যন্ত কাজ হয়েছে । এবং n পরিবর্তিত মান কে সে মনে রেখেছে । ফলে n = 1 এর সাথে 1 যোগ করে নতুন মান ২ রিটার্ন করেছে । এখন আমরা যদি আবার next(a) কল করি তবে সে একই কাজ করবে ।
সে কিন্তু n = 2 এর সাথে ১ যোগ করে ৩ রিটার্ন করেছে । আচ্ছা এখন আমরা যদি আবার next(a) কে কল করি তবে কি ঘটনা ঘটবে ? দেখা যাক রান করে দেখি কি ঘটে ।
Traceback এ দেখে যাচ্ছে যে StopIteration নামে একটা exception খাইছে । এর কারণ আমারা যে generator বানিয়েছি সেটা তো ৩ টা জিনিসই রিটার্ন করে । তো সেখান থেকে আমি কখনই ৪ টা জিনিস আশা করতে পারি না ।
উপরের ঘটনাগুলির সারাংশ এইরকম হতে পারে
- প্রত্যেক কল এ ভেরিয়েবল n এর মান মনে রাখা হয় ।
- সাধারণ ফাংশনের মত ভেলু রিটার্ন করার পরে লোকাল ভেরিয়েবল ধ্বংস হয়ে যায় না ।
- Generator অবজেক্ট কেবল একবারই iterate করা যায়
- নতুন করে iterate করা জন্য আমাদের কে a = my_generator() আবার কল করতে হবে ।
Python Generators কে লুপের মাধ্যমে iterate করা :
Python Generator Expression:
এটা হল ফাংশন ক্রিয়েট না করেই জেনারেটর অবজেক্ট ক্রিয়েট করা । আমাদের জেনারেটর ফাংশন যদি খুব ছোট হয় তবে সেটা ইমপ্লিমেন্ট করা জন্য আলাদা করে ফাংশন ডিফাইন করার দরকার নাই । ল্যামডা ফাংশন যেমন anonymous function ক্রিয়েট করতে পারে । ঠিক তেমনি ভাবে generator expression, anonymous generator ফাংশন ক্রিয়েট করতে পারে ।Generator expression এর সিনট্যাক্স list comprehension এর মতই শুধু পার্থক্য হল এখানে square brackts এর পরিবর্তে round parentheses ব্যবহার করা হয় । কিছু উদাহরণ দেখা যাক ।
কেন আমরা Generator ব্যবহার করব ?
অনেকের মনে হতে পারে যে জেনারেটরের কাজ তো সাধারণ ফাংশন ও লিস্ট কম্প্রিহেন্সনের মাধ্যমেই করা যায় তবে এটা দরকার টা কি ? এর বেশ কিছু সুবিধা আছে তার মধ্যে অন্যতমমেমরি খরচ অনেক কমায়ে দেয় :
প্রশ্ন হতে পারে যে মেমরি খরচ কিভাবে কমায়ে দেয় ? মনে করেন আমাদের একটা ফাংশন আছে যেটা 2GB ইমেজ ফাইল রিড করে একটা লিস্ট ভেরিয়েবলে স্টোর করে রাখে সব ফাইল রিড করা শেষ হলে লিস্ট টি রিটার্ন করে দেয় । আমাদের ফাংশন টা যদি সাধারণ ফাংশন হয় তবে এটা পুরা 2GB ইমেজের ডাটা কে মেমরিতে তে লোড করে রাখবে যতক্ষণ না সেটা ফাইনাল লিস্ট কে return করে ।কিন্তু আমরা যদি জেনারেটর ব্যবহার করি তবে আমাদের কে পুরা 2GB ইমাজের ডাটা কে মেমরিতে লোড করতে হচ্ছে না । কারণ আমরা দেখেছি যে জেনারেটর একক সময়ে কেবল একটাই আইটেম রিটার্ন করে ।
অসীম একটা Stream কে রিপ্রেজেন্ট করা যায়:
আপনার যদি অসীম সংখ্যক ডাটা থাকে তবে আপনি খুব সহজেই জেনারেটর দিয়ে সেটা কে রিপ্রেজেন্ট করতে পারবেন । কি বিশ্বাস হচ্ছে না ? আমরও অবশ্য শুরুতে বিশ্বাস হয় নি । কিন্তু এখন হয় । একটা উদাহরণ দেই তাহলেই বিশ্বাস হয়ে যাবে ।Fibonacci numbers এর সাথে আমরা অনেকেই পরিচিত । তো আমরা Fibonacci number এর অসীম stream generate করার চেষ্টা করি ।
বিশাল বড় দুইটা Fibonacci সংখ্যা দিয়ে দিছে । কি ভয়ঙ্কর সুন্দর ও সাঙ্ঘাতিক ব্যাপার । আপনার মেশিন যতটুকু লোড নিতে পারবে সেটা উপর ভিত্তি করে আপনি 10000, 10002 এইটা দুইটা মান ছোট ও বড় বসাতে পারেন । আমার মেশিনে এর থেকে বড় সংখ্যা দিলে মেশিনের অবস্থা খারাপ হয়ে যাবে ।
আপনার সিস্টেম সবচেয়ে বড় কি সংখ্যা দেয়া যেতে পারে তার জন্য আপনই sys.maxsize দিয়ে সাইজ দেখে নিতে পারেন ।
এই দুইটা বাদেও generator এর আরও কিছু আপ্লিকেশন আছে । সেগুলো জানার জন্য আপনি গুগল করতে পারেন । আজকে এ পর্যন্তই ।
মুল লেখা: https://github.com/menon92/Python-Learning/blob/master/python_generator_by_example.ipynb
Comments
Post a Comment