Skip to main content

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

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

জেনারেটর কি ?

পাইথনে 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 বানিয়েছি সেটা তো ৩ টা জিনিসই রিটার্ন করে । তো সেখান থেকে আমি কখনই ৪ টা জিনিস আশা করতে পারি না ।
উপরের ঘটনাগুলির সারাংশ এইরকম হতে পারে
  1. প্রত্যেক কল এ ভেরিয়েবল n এর মান মনে রাখা হয় ।
  2. সাধারণ ফাংশনের মত ভেলু রিটার্ন করার পরে লোকাল ভেরিয়েবল ধ্বংস হয়ে যায় না ।
  3. Generator অবজেক্ট কেবল একবারই iterate করা যায়
  4.  নতুন করে 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

Popular posts from this blog

[Python] *args vs **Kwargs

পাইথনে ফাংশন আর্গুমেন্ট *args ও **kwargs আমরা মাঝে মাঝেই পাইথনের ফাংশনের প্যারামিটার হিসাবে *args, ** kwargs কে দেখতে পাই । তো এগুলো আসলে কি ? এবং এগুলো কিভাবে কাজ করে ? এই দুইটা বুঝতে হলে প্রথমে আমাদের আর্গুমেন্ট কি সেটা বুঝতে হবে । আর্গুমেন্ট কোন ফাংশন কল করার সময় আমার ফাংশনে যে ভেলু pass করি করি সেটা কে বলা হয় আর্গুমেন্ট । পাইথনে দুই ধরনের আর্গুমেন্ট আছে, Keyword Argument. Positional Argument. Keyword Argument যে সকল আর্গুমেন্টের সাথে তার আইডেন্টিফায়ার থাকে সেসব আর্গুমেন্ট কে keyword argument বলে । উদাহরণ দিলে পরিষ্কার হয়ে যাবে। মনে করেন আমাদের এইরকম একটা ফাংশন আছে, উপরে আমরা ৪ নং লাইনে ফাংশন কল করার সময় যে দুইটা আর্গুমেন্ট pass করলাম সেটাকে বলা হয় keyword argument. এখানে name = 'Arif' এ name এবং age = 24 এর age আর্গুমেন্ট দুইটির আইডেন্টিফায়ার । কারণ name, age দিয়ে আমরা আলাদা ভাবে দুইটা আর্গুমেন্টকে আইডেন্টিফাই করতে পারছি । Positional Argument যে আর্গুমেন্ট গুলো keyword argument না সেগুলো কে positional argument বলে । এই ধরনের আর্গুমেন্টের কোন আইডেন্টি...