سلام به همه پایتون دوستانِ عزیز. توی این قسمت از یکبار برای همیشه سراغِ Iterator و شکل سادهتر اونها Generatorها رفتیم. در یکبار برای همیشه بحثی رو مطرح میکنیم که خیلی در نگاه اول شاید راحت بنظر نرسه، و اینجا سعی میکنیم با بیان سادهتر و مثالهای خیلی خیلی ابتدایی اون مطلب رو به راحتی به مخاطب انتقال بدیم.
عکس بالا یک کوآلا رو میبینم که از بومیهای استرالیاست و معروفِ به تنبلی؛ میگن نه تنها روزی بیست ساعت میخوابه، بلکه یه مقدار برگ هم توی لپش قایم میکنه واسه میانوعده! نکته جالب اینجاس که Iterator و generatorها هم یه همچین نقشی توی پایتون دارن. بازم مثل همیشه یک سری کلمات کلیدی داریم که اول اونارو معرفی میکنیم تا ادبیات همهمون برای ادامهی کار یکی بشه.
چون متد __next__ رو داره پس میتونیم تابع next رو روی اون صدا بزنیم تا مقدار بعدی رو به ما بده.
یک نکته هر آبجکتی که متد __iter__ داشته باشه یعنی قابلیت تبدیل شدن به Iterator رو داره و اگه بخوایم تبدیلش کنیم به Iterator میتونیم به یکی از دو روش پایین عمل کنیم:
توی خط آخر منظور من این بوده که خودِ numbers چون Iterator نیست پس صدا زدن next روی اون بی معنیِ. اما اینکه چرا پس ما میتونیم حلقه بذاریم و هربار یک ایتمش رو بگیریم به خاطر متد __getitem__ هست.
انگیزه ما از یادگیری: Iteratorها و فامیل خیلی نزدیکش Generatorها توی خیلی از بخشهای پایتون به کار رفتن من جمله:
تا اینجای کار بیشتر شبیه کلاس درس فلسفه شد و هدف من این بود که به یک حسی از Iteratorها و کلمات مهم این حوزه برسیم اما بریم سراغ اصل کار که عینِ آدم اینارو معرفی کنیم و بگیم کجاها کاربرد دارن.
خب، Generatorها در حقیقت خیلی شبیه توابع معمولی هستند با این تفاوت که دیگه از return استفاده نمیکنیم و به جاش از yield (بخونید ییِلد ر.ک به لینک) استفاده میکنیم.
فرض کنیم یه تابع داریم به اسم countdown که شروع میکنه میشماره تا به صفر میرسه. این رو توی دوحالت Generator و تابع معمولی تعریف میکنیم:
بریم از این دو تا تابع استفاده کنیم ببینیم چه فرقهایی دارند:
تابع اول به محض فراخوانی تمام کارش رو انجام داده و خروجی رو داخل متغییر numbers ریخته. برعکس اون تابع دوم انگار هیچ کاری نکرده فقط انگار یک آبجکتی به نام Generator برگردونده:
نکات مهم:
بعضی از دوستان معتقدند که استفاده از Iterator و Generator باعث میشه کد ما خواناتر بشه. برای توضیح دادن این بخش فرض کنیم قصد داریم که تابع سری فیبوناچی رو به صورت Generator پیادهسازی کنیم.
حالا اگه یکی به ما گفت مثلا من ده عدد اول سری فیبوناچی رو میخوام چی؟ تو حالت اول که سریع میریم داخل تابع یک متغییر تعریف میکنیم و میشماریم اگه ده شد بزن بیرون. اما این راهحل خیلی قشنگ نیست، اگه بخوایم فانکشنال فکر کنیم میگیم همین حالت که هست باشه، با ابزار دیگه این کار رو انجام بدیم. اینجاست که یک ماژول خیلی باحال به اسم itertools به کمک ما میاد( اگه دنبال مثال بیشتری برای این بخش هستید هر تابعِ داخل itertools یک مثال هست).
نکته: هر موقع بخواهید به همه مقادیر یک Generator یا Iterator دسترسی داشته باشید کافیه مشابه مثال بالا اون رو تبدیل به list کنید.
تابع dropwhile دو ورودی میگیره یک شرط پیشبینی هست و دومی iterable آبجکت.
این ماژول خیلی چیزهای باحالی داره مثلا اگه میخواهید تمام حالتهای ترکیب چند کاراکتر رو بگیرید و هر بار به یک آیتم دسترسی داشته باشید میتونید از permutations کمک بگیرید. یک تابع خیلی باحال به اسم product داره که حتماً بهش نگاه بندازید.
انتظار ما این هست که با پیدا کردن عدد مورد نظر برنامه متوقف بشه ولی خب این کار صورت نمیگیره و حلقهها تا آخرین حالت ممکن اجرا میشن( حتما این بخش رو خودتون بنویسید و اجرا کنید)، این در صورتی هست که با Generatorها مطمئنیم به محض اینکه عدد مورد نظر پیدا شد دیگه محاسبات بیهوده نداریم.
خُب، اگه یادتون باشه گفتیم Generator یک شکل خیلی خیلی ساده شدهی Iteratorها هستند. دقیقاً مشابه این هست که داریم یک کار رو از صفرِ صفر شروع میکنیم (Build from scratch). مثال سری فیبوناچی رو یادتون هست که با Generator نوشتیم؟ این بار میریم اون رو با یک تغییر کوچیک به سبک Iterator بنویسیم و میفهیم منظورمون از Build from scratch چیه؟
اولین تغییری که به چشم میاد، استفاده از کلاس هست به جای مثالهای قبلی که همگی تابع بودند. و دو تابع خاص منظوره رو استفاده کردیم که عبارتند از : __iter__ و __next__.
حالا فکر کنم بیشتر به قدرت yield پی بردیم، ساخت یک Iterator با Generator بسیار راحتتر میشه. نکته کلیدی قدرت سفارشیسازی Iterator است در مقابل سادگیِ Generator و resource-friendly بودن این دوتا دوست عزیز ما در پایتون!
امیدوارم این قسمت یکبار برای همیشه حق مطلب رو بخوبی ادا کرده باشه. اگه کسی رو میشناسید که دوست داره پایتون رو به صورت حرفهای یاد بگیره یادتون نره این پستها رو باهاش به اشتراک بذارید. خیلی ممنون تا قسمت بعدی یکبار برای همیشه خدانگهدار!
اگر بازم حس میکنید نیاز به مطالعه بیشتر در این زمینه دارید:
http://www.diveintopython3.net/generators.html
http://www.diveintopython3.net/iterators.html
* اگر از itertools خوشتون اومده حتما این لینک رو هم دنبال کنید کلی مثال جذاب داره!
https://realpython.com/python-itertools/