Generator در پایتون چیست؟ – به زبان ساده + سینتکس و مثال جنریتور

۸۱
۱۴۰۴/۰۸/۲۴
۱۹ دقیقه
PDF
آموزش متنی جامع
امکان دانلود نسخه PDF

از Generator در پایتون برای ساخت مجموعه‌ای از داده‌ها استفاده می‌شود. این تابع به‌جای عبارت return در پایتون، داده‌های خود را با عبارت yield  برمی‌گرداند. توابع Generator تمام عناصر خود را یکجا تولید نمی‌کنند. بلکه هر بار که اجرا می‌شوند فقط یک عنصر از توالی مورد نظر را تولید کرده و به خروجی ارسال می‌کنند. فرض کنید که در حال کار بر روی پروژه علم داده هستید. در این پروژه باید بر روی مجموعه داده بسیار بزرگی کار کنید. این مجموعه داده به حدی بزرگ است که بارگذاری آن بر روی حافظه باعث کرش کردن کامپیوتر می‌شود. در چنین پروژه‌ای امکان ذخیره همه چیز به صورت همزمان وجود ندارد. یکی از بهترین روش های حل این چالش استفاده از Generator-ها در پایتون است.

آنچه در این مطلب می‌آموزید:
  • با کمک مثال ساده‌ای روش تعریف و استفاده از Generator در پایتون را می‌آموزید.
  • با سینتکس و روش‌های مختلف تعریف جنریتور آشنا شده و کاربرد yield را درک می‌کنید.
  • متوجه می‌شوید که مزایای اصلی استفاده از Generator در پایتون چه هستند.
  • با چند مورد از متدها و مفاهیم پیشرفته Generator آشنا می‌شوید.
  • با چند کاربرد Generator در پروژه‌های واقعی آشنا شده و مشکلات استفاده از آن را یاد می‌گیرید.
  • به راحتی می‌توانید تفاوت بین جنریتور و لیست را از جهت سرعت و مصرف حافظه تشخیص بدهید.
Generator در پایتون چیست؟ – به زبان ساده + سینتکس و مثال جنریتورGenerator در پایتون چیست؟ – به زبان ساده + سینتکس و مثال جنریتور
فهرست مطالب این نوشته
997696

در این مطلب از مجله فرادرس با Generator در پایتون آشنا می‌شویم. ابتدا این نوع خاص از تابع را تعریف کرده و روش استفاده از آن را بررسی می‌کنیم. سپس چند مفهوم پیشرفته مربوط به Generator را توضیح می‌دهیم. در ادامه متدهای ویژه جنریتور در پایتون، چند کاربرد آن در دنیای واقعی و مزایا و معایب استفاده از این ابزار برنامه نویسی را هم بررسی می‌کنیم.

Generator در پایتون چیست؟

Generator-های پایتون نوع خاصی از توابع یا عبارت‌های فشرده‌ای هستند که توالی از مقادیر را تولید می‌کنند. روش کار آن‌ها به این صورت است که تمام مقادیر را هم‌زمان نمی‌سازند. بلکه فقط بعد ازاعلام درخواست، یک به یک جواب‌ها را پشت سرهم می‌سازند.

Generator-ها را مانند نوار نقاله در کارخانه در نظر بگیرید. به جای پشته‌سازی تمام محصولات در فضای یکسان و روبه‌رو شدن با کمبود جا، روی هر محصول درست در همان زمانی کار می‌کنید که از نوار نقاله می‌رسد. به خاطر این ویژگی Generator-ها در پایتون، مصرف حافظه به مقدار خیلی زیادی کم‌ می‌شود. آن‌ها بخشی از سیستم پیمایش‌گر پایتون هستند. از این روش می‌توان در بخش‌های مختلف پایتون استفاده کرد. برای نمونه، می‌توان آن‌ها را در حلقه for یا در روش «List Comprehension» برای ساخت سریع لیست‌ها به کار برد.

قدرت ویژه Generator-ها به خاطر وجود کلمه کلیدی yield  است. می‌دانیم که در توابع سنتی، دستور return مقداری را در خروجی برگردانده و سپس کار تابع را به طور کامل متوقف می‌کند. اما دستور yield  هربار که تابع اجرا می‌شود، فقط یک مقدار برمی‌گرداند. سپس اجرای تابع را به صورت موقت، متوقف می‌کند. yield  تمام مشخصات تابع را در زمان توقف به خاطر می‌سپارد. وقتی Generator کار خود را دوباره شروع کند، دقیقا از همان جایی کار را ادامه می‌دهد که قبلا متوقف کرده بود.

نمودار عملکرد تابع Generator در پایتون
تا وقتی که yield امکان تولید داده داشته باشد، تابع Generator کار می‌کند.

برای مثال فرض کنید که در حال خواندن فایل گزارشات بسیار حجیمی به صورت خط‌به‌خط هستید. وظیفه Generator این است که هر خط را به محض خواندن بارگذاری کند. بدون اینکه مجبور باشد تمام فایل را به یکباره در حافظه بارگذاری کند. به این کار «ارزیابی تنبل» (Lazy Evaluation) گفته می‌شود. تکنیک ارزیابی تنبل Generator-ها را نسبت به توابع معولی برجسته‌تر می‌کند. با این تکنیک Generator به ابزار بسیار خوبی برای انجام وظایفی تبدیل می‌شود که کارآمدی و صرفه‌جویی از حافظه اهمیت دارد.

Generator یکی از مفاهیم پیشرفته و حرفه‌ای پایتون است که در مجله فرادرس به توضیح آن پرداخته‌ایم. برای دسترسی به سایر مطالب مجله به‌خصوص در حوزه برنامه نویسی با پایتون پیشنهاد می‌کنیم از اپلیکیشن مجله فرادرس استفاده کنید.

برای نصب اپلیکیشن رایگان مجله فرادرس، کلیک کنید.

مثال ساده ای از Generator-ها در پایتون

برای درک بهتر موضوع باید روش نوشتن کدهای Generator و استفاده از آن را بررسی کنیم. در کادر زیر، نمونه‌ای از توابع Generator را پیاده‌سازی کرده‌ایم. این تابع وظیفه دارد n  عدد صحیح تولید بکند.

بعد از اجرای کدهای بالا خروجی پایین تولید شده و در کنسول پایتون نمایش داده می‌شود.

0
1
2
3
4

سینتکس و الگوهای Generator در پایتون

با دو روش مختلف می‌توانیم Generator تعریف کنیم.

  • توابع Generator
  • عبارت‌های Generator
انواع تعریف Generator در پایتون

در این بخش از مطلب هر کدام از روش‌های نام برده شده را همراه با نمایش سینتکس بررسی کرده‌ایم.

توابع Generator

توابع Generator مانند توابع معمولی پایتون تعریف می‌شوند. تنها تفاوت آ‌ن‌ها در استفاده از کلمه کلیدی yield  به جای دستور return  در پایتون است. وقتی از کلمه yield  استفاده کنیم، در خروجی تابع، شیء Generator برگشت داده می‌شود. این شیء پیمایش‌پذیر - تکرارگر - است.

بعد از اجرای کدهای بالا خروجی پایین تولید شده و در کنسول پایتون نمایش داده می‌شود.

1
2
3
4
5

در مثال بالا، وقتی تابع count_up_to()  را اجرا کردیم، در خروجی شیء Generator برگردانده شد. هربار که حلقه for  به دنبال مقدار جدیدی می‌گردد، تابع اجرا می‌شود تا به دستور yield  برسد. در آن لحظه، مقدار فعلی متغیر count  به بیرون از تابع ارسال می‌شود. سپس وضعیت فعلی تابع و تمام متغیر‌های آن ذخیره شده و منتظر شروع دور بعدی حلقه می‌شوند. وقتی حلقه دوباره اجرا شود، این تابع کار خود را دقیقا از همان‌جایی ادامه می‌دهد که دفعه پیش متوقف شده بود.

عملکرد yiekld در Generator در پایتون برای تولید داده

عبارت‌های Generator

عبارت‌های Generator، به کدهای کوتاه و فشرده‌ای هستند که با آن‌ها تابع Generator تعریف می‌کنیم. این عبارت‌ها تقریبا ساختاری شبیه به «List Comprehension» دارند. اما در آن‌ها به‌جای «براکت» یا []  از پرانتز ()  استفاده می‌شود.

بعد از اجرای کدهای بالا خروجی پایین، در کنسول پایتون نمایش داده می‌شود.

0
1
4
9
16

برای درک تفاوت‌های بین List Comprehension و عبارت Generator به نکات زیر توجه کنید.

  • دستور List Comprehension، لیست کاملی را ایجاد کرده و در حافظه ذخیره می‌کند.
  • عبارت Generator در هر لحظه فقط یک مقدار را تولید می‌کند. بنابراین مصرف حافظه بسیار کمتری نسبت به List Comprehension دارد.

جنریتور‌های پایتون در مقابل تکرارگرها

«تکرارگرهای» (Iterators) سنتی در پایتون به کلاس‌هایی نیاز دارند که شامل متد‌های __iter__()  و __next__()  باشند. این کار به میزان زیادی کد اضافی و بررسی دائمی و دستی وضعیت متغیرهای مختلف نیاز دارد. در حالی که توابع Generator hk[hl این کار را راحت‌تر کرده‌اند. زیرا آن‌ها به صورت خودکار وضعیت فعلی تابع را به خاطر می‌سپارند. بنابراین به این متد‌ها نیازی ندارند. برای مثال، تابع Generator ساده‌ای می‌تواند جواب مسئله «مربع تمام اعداد از ۱ تا n» را یک به یک و بدون مشکل به خروجی ارسال کند.

یادگیری پایتون با کمک فیلم‌های فرادرس

زبان پایتون به دلیل انعطاف‌پذیری بالا، قدرت و کتابخانه‌های متنوع، تقریبا بیشتر علاقه‌مندان را به خود جذب می‌کند. افراد زیادی برای دستیابی به موقعیت‌های شغلی مورد علاقه‌شان، زمان و انرژی خود را در راه آموزش پایتون صرف می‌کنند. داشتن استاد یا دسترسی به منابع با کیفیت، تاثیر زیادی در یادگیری درست برنامه نویسی با پایتون دارد. به همین دلیل باید سعی کنیم که از بهترین منابع به شکل درست استفاده کنیم.

مجموعه فیلم‌های آموزش برنامه نویسی پایتون Python – مقدماتی تا پیشرفته فرادرس
با کلیک بر روی تصویر بالا می‌توانید به صفحه اصلی مجموعه فیلم‌های آموزش برنامه نویسی پایتون Python از مقدماتی تا پیشرفته هدایت شوید.

فرادرس بزرگترین مرجع تولید محتوای آموزشی آنلاین به زبان فارسی است. این وب‌سایت تمرکز خاصی بر روی تولید محتوای مربوط به پایتون دارد. مطالب و فیلم‌های آموزشی در وب‌سایت فرادرس، تولید شده‌اند. فیلم‌های فرادرس، مطالب پیشرفته پایتون را با زبانی رسا و قابل درک آموزش می‌دهند. استفاده از فیلم‌های آموزشی فرادرس یکی از مقرون‌به‌صرفه‌ترین روش‌ها برای پایتون است. در پایین، چند مورد از فیلم‌های آموزشی مربوط به پایتون را معرفی کرده‌ایم.

متصل کردن Generator-ها به همدیگر

Generator-ها را می‌توان با همدیگر ترکیب کرد. برای مثال می‌توانید برای تغییردادن، فیلتر کردن و پردازش داده‌ها به صورت مرحله‌به‌مرحله جنریتور‌ها را به همدیگر متصل کنید.

فرض کنید توالی بینهایتی از اعداد داریم. تصمیم گرفته‌ایم که تمام اعداد را به توان دو برسانیم و اعداد فرد را از نتیجه حذف کنیم.

بعد از اجرای کدهای بالا خروجی پایین، در کنسول پایتون نمایش داده می‌شود.

0
4
16
36
64
100
144
196
256
324

این برنامه از سه تابع Generator تشکیل شده است. در فهرست زیر، هر سه آن‌ها را معرفی کرده‌ایم.

  • infinite_sequence(): این تابع بدون توقف تا بینهایت عدد، تولید می‌کند.
  • square_numbers(): وظیفه این تابع محاسبه مقدار مربع اعداد داده شده است.
  • filter_evens(): تابع filter_evens() تمام اعداد فرد ساخته شده را حذف کرده و فقط اعداد زوج را نگهداری می‌کند.

مفاهیم پیشرفته Generator در پایتون

اکنون وقت آن است که چند ایده پیشرفته‌تر را بررسی کنیم. در این بخش، یاد می‌گیریم چطور Generator‌-ها را با هم ترکیب کنیم و متدهای خاصی مثل .send() و .throw() و .close()  را به کار ببریم.

متدهای پیشرفته برای کار با Generator در پایتون
متدهای پیشرفته Generator در پایتون

متد send

توسعه‌دهندگان با کمک متد send()  می‌توانند مقادیری را به جنریتور برگردانند. به این ترتیب، ساختاری مانند شبکه میان جنریتور و تابع ایجاد می‌شود. به این ساختار، «کوروتین» (Coroutine) می‌گویند. این ویژگی در ساخت جنریتورهای تعاملی و «حالت‌دار» (Stateful) به کار برده می‌شود.

روش کار کدهای بالا را در فهرست پایین توضیح داده‌ایم.

  1. با اجرای next(acc)، Generator برای اولین بار فعال می‌شود و تا عبارت yield  پیش می‌رود.
  2. هر بار که از acc.send(value)  استفاده می‌کنیم، مقدار جدیدی به Generator فرستاده می‌شود و در متغیر value  قرار می‌گیرد.
  3. اگر مقدار فرستاده شده None  نباشد، همان عدد به متغیر total اضافه می‌شود.
  4. سپس Generator مقدار به‌روزشده‌ total را بازمی‌گرداند.
برنامه نویسی که در حال کار با کامپویتر است. مانیتور زرد رنگی دارد.

متد throw

با کمک متد throw()  در داخل جنریتور می‌توانید خطای مربوط به حالت‌های استثنا را اعلام کنید. این روش هم در بحث مدیریت خطا بسیار مفید است و هم برای اعلام شرایط خاص به توسعه‌‌دهنده و برنامه کمک می‌کند.

روش کار کدهای بالا را در فهرست پایین توضیح داده‌ایم.

  • این جنریتور به صورت عادی اعداد 0 تا 4 را تولید می‌کند.
  • مفسر پایتون به تابع .throw() برخورد می‌کند، خطای ValueError  در داخل جنریتور ایجاد می‌شود.
  • اما Generator می‌تواند این خطا را با استفاده از بلوک Try-Except در پایتون مدیریت کند. Generator، خطا را دریافت کرده و به‌جای توقف برنامه، مقدار "Error occurred!"  را برمی‌گرداند.

متد close

متد close()  از طریق اعلام خطای استثنای GeneratorExit  باعث توقف کار Generator می‌شود. از این ویژگی برای انجام کارهایی مانند پاک‌کردن منابع یا متوقف کردن Generator-های بی‌نهایت به کار برده می‌شود.

در فهرست پایین، روش کار کدهای بالا را توضیح داده‌ایم.

  1. این Generator می‌تواند بدون هیچ مشکلی تا بی‌نهایت عدد تولید بکند.
  2. در خط ۱۵ با متد .close()  انجام کار آن را متوقف کرده‌ایم.
  3. هر وقت، مفسر پایتون با متد .close()  روبه‌رو شود، حالت استثنای GeneratorExit  را اعلام کرده و فعالیت Generator را متوقف می‌کند.

با کمک بلوک try-except  در پایتون می‌توانیم از این وضعیت برای پاک‌سازی منابع یا اعلام پیغام مناسب - گزارش‌گیری - قبل از پایان کار تابع استفاده کنیم.

کاربرد Generator در پروژه‌های دنیای واقعی:‌ علم داده

تا اینجای مطلب به خوبی با Generator در پایتون آشنا شدیم. همچنین، روش نوشتن و استفاده از آن را یاد گرفتیم. در این قسمت با کاربرد‌های Generator در دنیای واقعی بخصوص حوزه علم داده آشنا می‌شویم. در صورت نیاز به آشنایی بیشتر با علم داده پیشنهاد می‌کنیم فیلم آموزش رایگان علم داده یا دیتا ساینس Data Science چیست؟ همراه با معرفی فرصت‌های شغلی را در فرادرس مشاهده کنید. لینک دسترسی به این فیلم در پایین نیز قرار داده شده است.

پردازش مجموعه داده‌های بزرگ

یکی از بزرگترین چالش‌های دیتا ساینس مربوط کار با مجموعه‌داده‌های خیلی بزرگ است. خیلی وقت‌ها در این حوزه با مجموعه‌ای از داده‌ها روبه‌رو می‌شویم که بزرگتر از اندازه حافظه کامپیوتر هستند. برای پردازش چنین داده‌هایی، جنریتور‌ها ابزار خوبی هستند. زیرا می‌توانند خط به خط داده‌ها را خوانده و پردازش کنند.

فرض کنیم که فایلی با پسوند «csv.» و حجم ۱۰ گیگابایت داریم. این فایل شامل اطلاعات فروش کالا و خدمات در دوره‌ی زمانی مشخصی است. اکنون می‌خواهیم داده‌های مربوط به منطقه جغرافیایی خاصی را فیلتر کنیم. در کادر پایین، روش استفاده از پایپ‌لاین جنریتور را برای رسیدن به این هدف، بررسی کرده‌ایم. در صورتی که با کاربرد فایل‌های CSV در پایتون آشنا نیستید، پیشنهاد می‌کنیم مطلب مربوط به آن را در مجله فرادرس مطالعه کنید.

در فهرست پایین، مهم‌ترین بخش‌های کد بالا را توضیح داده‌ایم.

  • تابع read_large_csv(): در تابع read_large_csv()  فایل مورد نظر به صورت خط به خط خوانده می‌شود. سپس داده‌های هر ردیف به شکل دیکشنری در خروجی برگردانده می‌شوند.
  • تابع filter_by_region(): این تابع، ردیف‌های رسیده به آن را بر اساس ناحیه مشخص شده فیلتر می‌کند.

این پایپ‌لاین داده‌ها را خط به خط پردازش می‌کند. در نتیجه از بروز سرریز در حافظه جلوگیری می‌شود.

مهم‌ترین کاربرد Generator در پایتون برای پردازش حجم انبوه داده است.

این تکنیک برای استفاده در «جریان‌های کاری» (Workflows) مشهور به ETL گزینه خوبی است. ETL مخفف عملیات «استخراج» (Extract)، «تبدیل» (Transform) و «بارگذاری» (Load) است. در این جریان‌های کاری باید داده‌ها را قبل از تحلیل، پاکسازی و پیرایش بکنیم.

استریم کردن و پایپ لاین‌ها

استریم کردن به ایجاد جریان دائم و زنده داده‌ گفته می‌شود. برای مثال می‌توان به پخش زنده تصاویر ویدئویی اشاره کرد. گاهی از اوقات باید به شکل جریان زنده و فعال - به صورت مداوم - داده‌ها تولید و مصرف بشوند. به عنوان نمونه می‌توان به داده‌های در حال دریافت از سنسور‌ها، گزارش‌های زنده تلویزیونی یا پخش زنده تصاویر در شبکه‌های اجتماعی اشاره کرد.

فرض کنید که در حال کار با دستگاهی هستیم که به اینترنت اشیاء متصل است. این دستگاه هر ثانیه داده‌های مربوط به دمای محیط را جمع‌آوری کرده و به سیستم ارسال می‌کند. در این برنامه می‌خواهیم داده‌های جمع‌آوری شده را به گروه‌های ۱۰-تایی تقسیم کنیم. سپس میانگین دما را برای هر گروه محاسبه کرده و در جایگاه قرار بدهیم. یعنی مقدار دما به ازای هر ثانیه را به میانگین دما در هر ده ثانیه تبدیل کنیم.

در فهرست پایین مهم‌ترین بخش‌های کد بالا را توضیح داده‌ایم.

  • تابع sensor_data_stream(): این تابع، جریان دائمی از داده‌های خوانده شده توسط سنسور دما را پیش‌بینی می‌کند. در واقع به صورت تصادفی تولید می‌کند.
  • تابع sliding_window_average(): تابع sliding_window_average()  به طور دائم داده‌ها را به صورت ده‌تایی دسته‌بندی کرده و در حافظه ذخیره می‌کند. سپس مقدار میانگین آن‌ها را محاسبه کرده و در خروجی برمی‌گرداند.

این پایپ‌لاین، جریان داده ورودی را به صورت لحظه‌ای پردازش می‌کند. در نتیجه گزینه بسیار خوبی برای نظارت و تحلیل داده‌ها است.

پایتون با کمک Generator در حال دریافت داده از سنسور‌های دماسنج است.

شبیه‌سازی و وب اسکرپینگ

از جنریتورهای پایتون می‌توان در موقعیت‌های دیگری هم استفاده کرد. مثلا زمانی که اندازه مجموعه داده مشخص نیست، یا وقتی که داده‌ها به طور پیوسته و بدون توقف به مجموعه اضافه می‌شوند. در این بخش از مطلب دو مورد از این سناریو‌ها را شبیه‌سازی کرده‌ایم.

وب اسکرپینگ

در زمان اجرای عملیات وب‌اسکرپینگ اغلب اوقات به طور دقیق مشخص نیست که توسعه‌دهندگان باید چند صفحه وب یا عنصر آنلاین را پردازش کنند. با کمک Generator در پایتون می‌توانیم این موقعیت‌های غیرقابل پیش‌بینی را به طرز بسیار خوبی مدیریت کنیم.

در فهرست پایین، مهم‌ترین بخش‌های کد بالا را توضیح داده‌ایم.

  1. تابع scrape_website(url): این تابع به صورت Generator و برای تولید صفحات اینترنتی، پشت سر هم، تعریف شده است.
  2. حلقه while url:: تا وقتی آدرس صفحه وجود دارد، اجرا می‌شود.
  3. دستور print(f"Scraping {url}"): فقط نشان می‌دهد که کدام صفحه در حال پردازش است.
  4. data = f"Data from {url}": این خط کد، داده ساختگی از همان صفحه می‌سازد.
  5. کد yield data: این عبارت، داده را برمی‌گرداند. با رسیدن به این خط، اجرای تابع به صورت موقت متوقف شده و وضعیت فعلی متغیر‌ها ذخیره می‌شود.
  6. get_next_page(url) : سپس get_next_page(url)  آدرس صفحه بعدی را می‌گیرد. اگر صفحه‌ای نبود، حلقه تمام می‌شود.
  7. در پایین، scraper = scrape_website("https://example.com/page1")  تابع Generator را می‌سازیم.
  8. حلقه for data in scraper: این حلقه، تابع Generator را اجرا کرده و هر بار داده جدیدی چاپ می‌کند تا وقتی تمام صفحات بررسی شوند.

شبیه سازی

در انجام پرو‌ژه‌های شبیه‌سازی مانند اجرای «متد‌های مونت کارلو» (Monte Carlo methods) یا عملیات مربوط به توسعه بازی‌، جنریتور‌ها می‌توانند کارهای مربوط به توالی‌های پویا یا بی‌نهایت را شبیه‌سازی کنند.

در فهرست پایین مهم‌ترین بخش‌های کد بالا را توضیح داده‌ایم.

  1. تابع monte_carlo_simulation()  به صورت Generator تعریف شده است.
  2. در داخل تابع، ماژول random  را ایمپورت کردیم.
  3. Generator با کمک حلقه while True:  می‌تواند بی‌نهایت عدد بسازد.
  4. دستور yield random.random()  هر بار عددی تصادفی بین ۰ و ۱ برمی‌گرداند.
  5. با کد ()simulation = monte_carlo_simulation تابع Generator را ایجاد کرده و نتیجه را در متغیر simulation  ذخیره می‌کنیم.
  6. حلقه for _ in range(10):  ده بار اجرا می‌شود.
  7. هر بار با next(simulation)  عدد تصادفی جدیدی ساخته و آن‌ را چاپ می‌‌کنیم.

مقاسیه سرعت و حافظه

در اجرای بعضی از وظایف، میزان حافظه مصرف شده و روش مصرف حافظه از اهمیت بسیار زیادی برخوردار هستند. توابع Generator بخاطر روش کار خاص خود، در انجام این وظایف عملکرد درخشانی نشان می‌دهند. اما این ابزارها همیشه سریع‌ترین گزینه در دسترس نیستند. برای درک بهتر مشکلات جنریتور‌ها در این بخش از مطلب،‌ آن‌ها را با لیست‌ها مقایسه کرده‌ایم.

مقایسه روش ساخت و نگهداری عناصر بین لیست‌ها و Generator در پایتون
لیست‌ همان ابتدا تمام عناصر را ساخته و در حافظه ذخیره می‌کند. اما Generator یک به یک و در زمان اعلام درخواست، عنصر تولید می‌کند.

در بخش‌های بالاتر دیدیم که Generator در مقایسه با لیست، حافظه را بسیار بهتر مدیریت می‌کند. این نکته در زمان محاسبه مقدار حافظه مصرف شده برای تولید توالی از ۱۰ میلیون عدد، مشخص شد. اکنون باید کار متفاوتی انجام بدهیم. می‌خواهیم سرعت اجرای عملیات را توسط این دو ساختار مقایسه کنیم.

بعد از اجرای کدهای بالا خروجی پایین، در کنسول پایتون نمایش داده می‌شود.

List comprehension time: 0.1234 seconds
Generator expression time: 0.1456 seconds

در بررسی بالا مشخص می‌شود که Generator در پایتون نسبت به لیست‌، سرعت پایین‌تری دارد. با اینکه در مصرف حافظه بسیار بهینه‌تر عمل می‌کند اما در زمان اجرای پروژه‌ها باید به سرعت اجرای برنامه‌ هم توجه کرد. دلیل اصلی این اتفاق آن است که در Generator-های پایتون زمان بیشتری برای توقف و ادامه عملیات Generator در هربار اجرای آن صرف می‌شود.

مقایسه بین لیست و Generator در پایتون با کمک ترازو

در پایگاه‌های داده کوچک‌تر اختلاف عملکرد این دو ابزار بسیار کم است. اما در زمان کار با مجموعه داده‌های بزرگ، بیشتر مواقع، مقدار حافظه صرفه‌جویی شده در مقایسه با تأخیر کوچک اجرای برنامه، ارزش بیشتری دارد.

مزایا و معایب استفاده از Generator در پایتون

در این قسمت از مطلب Generator در پایتون، مزایا و معایب استفاده از این ابزار جالب را بررسی می‌کنیم. Generator-ها فقط توابع فانتزی و تجملی نیستند. این توابع می‌توانند برای مسائل واقعی راه‌‌‌حل‌های مفیدی ارائه بدهند. اما قبل از کار با آن‌ها باید به نکات گفته شده در این بخش توجه کرد.

در جدول پایین مهم‌ترین مزایا و معایب استفاده از Generator در پایتون را نوشته‌ایم.

مزایامعایب
استفاده بهینه از حافظهتمام شدنی بودن Generator-ها
افزایش عملکردنقطه ضعف ارزیابی تنبل
سادگی و خواناییزمان اجرای بیشتر
مدیریت توالی‌های بی‌نهایت

در ادامه، تمام نکات نوشته شده در جدول بالا را توضیح می‌دهیم.

مزایای استفاده از Generator در پایتون

استفاده از این مفهوم در زبان برنامه نویسی پایتون نکات مثبت بسیار مهمی دارد. در این بخش از مطلب این نکات را توضیح داده‌ایم.

استفاده بهینه از حافظه

لیست‌ها و آرایه‌های پایتون تمام عناصر خود را به صورت همزمان در حافظه ذخیره می‌کنند. Generator برعکس این ساختارها عمل می‌کند. یعنی اینکه در هر لحظه فقط یک مقدار را تولید کرده و به خروجی ارسال می‌کند. در واقع هر لحظه فقط همان مقدار در حافظه ذخیره می‌شود. برای مثال به تفاوت بین توابع range و xrange()  در پایتون ۲ توجه بکنید.

  • range():‌ در پایتون ۲ این تابع، محدوده خود را به شکل لیست در حافظه ذخیره می‌کند. این روش در زمان تعریف کردن محدوده‌های بزرگ شاید مشکل‌ساز شود.
  • xrange() : این تابع مانند Generator-ها عمل می‌کند. مقادیر را به صورت یک به یک آماده می‌کند. هر مقدار در همان لحظه‌ای تولید می‌شود که درخواست شده است.

از آنجا که عملکرد xrange()  در پایتون ۲ بسیار موفقیت‌آمیز بود، در پایتون ۳ هم عملکرد تابع range()  مانند جنریتور‌ها شده است. بنابراین با اجتناب از ذخیره تمام مقادیر به صورت همزمان، احتمال سرریز شدن حافظه از بین رفت.

برای بررسی این مسئله، در کدهای پایین مقدار حافظه مصرف شده در زمان تولید توالی از ۱۰ میلیون عدد مختلف را با همدیگر مقایسه کرده‌ایم.

بعد از اجرای کدهای بالا خروجی پایین، در کنسول پایتون نمایش داده می‌شود.

Memory used by list: 89.48 MB
Memory used by the generator: 112 bytes

همین‌طور که از نتیجه اجرای کدهای بالا مشخص است، می‌توان گفت، Generator-ها در مقایسه با لیست‌ها تقریبا هیچ حافظه‌ای مصرف نمی‌کنند. این تفاوت واقعا چشم‌گیر است.

افزایش عملکرد

به خاطر استفاده از تکنیک «ارزیابی تنبل» (Lazy Evaluation) هر مقدار دقیقا همان‌ لحظه‌ای ساخته می‌شود که نیاز است. یعنی آن که بدون نیاز به صبر برای ساخته شدن تمام عناصر توالی، می‌توانید پردازش داده‌های خود را شروع کنید.

نماد پایتون و چند مربع که با فلش به همدیگر اشاره می‌کنند.

برای مثال، فرض کنید می‌خواهیم نتیجه جمع مربع اعداد ۱ تا ۱۰۰۰۰۰۰ را محاسبه کنیم.

در کدهای بالا هر دو روش نتیجه یکسانی برمی‌گردانند. اما از آن‌جا که کدهای نوشته شده با Generator لیست حجیمی نمی‌سازند، در نتیجه با سرعت بیشتری پاسخ را تولید می‌کنند.

سادگی و خوانایی

با کمک Generator در پایتون ساختن «تکرارگرها» ساده‌تر می‌‌شود. زیرا دیگر لازم نیست مقدار زیادی کدنویسی کنیم. برای درک بهتر تفاوت این تکنیک‌ها باید تکرارگری که با کلاس تعریف شده را با تابع جنریتور مقایسه کنیم.

در کادر پایین، تکرارگر SquaresIterator را با کمک کلاس‌های پایتون تعریف کرده‌ایم.

در کادر زیر هم نسخه شبیه به همین عملیات را با کمک تابع Generator پیاده‌سازی کرده‌ایم.

می‌بینید که نسخه ساخته شده با تابع جنریتور کوتاه‌تر و خواناتر است، زیرا با کدهای کمتری نوشته شده. در واقع، تابع Generator به عنوان نمونه خوبی برای فلسفه پایتون یعنی «سادگی بهتر است» محسوب می‌شود.

مدیریت توالی‌های بی‌نهایت

Generator بهترین گزینه برای ساخت توالی‌های بی‌نهایت در پایتون است. ساخت این توالی‌ها با کمک دیگر ساختار‌ها مانند لیست‌های پایتون اصلا امکان ندارد. برای نمونه، سری فیبوناچی را در نظر بگیرید.

بعد از اجرای کدهای بالا خروجی پایین، در کنسول پایتون نمایش داده می‌شود.

0
1
1
2
3
5
8
13
21
34

این ابزار می‌تواند بدون مشکل در کار با حافظه، تمام عناصر فیبوناچی را تا بی‌نهایت تولید بکند. به عنوان چند مثال دیگر می‌توانیم به انجام وظایف مربوط به پردازش جریان‌های زنده داده یا انجام عملیات بر روی سری‌های زمانی اشاره کنیم.

معایب استفاده از Generator در پایتون

Generator‌-های پایتون هم مانند باقی ابزارهای کدنویسی نقاط ضعف و مشکلات خاص خود را دارند. در این بخش از مطلب، چند مورد از مشکلات مربوط به استفاده از Generator در پایتون را بررسی کرده‌ایم.

Generator-ها تمام شدنی هستند

بعد از اینکه Generator یک بار به طور کامل مصرف شد، دیگر قابل استفاده نیست. برنامه نویس اگر بخواهد که دوباره بر روی آن پیمایش کند، باید از ابتدا Generator خود را بسازد.

در کدهای بالا روش استفاده از عبارت Generator نمایش داده شده است.

  1. در خط اول، عبارت Generator را تعریف کرده‌ایم. این عبارت اعداد 0  تا 4 را تولید می‌کند. جنریتور نوعی تکرارگر است که در هر لحظه - و فقط به شرط درخواست از آن - یک عنصر تولید می‌کند.
  2. در عبارت print  اول، داده‌های تولید شده توسط Generator به لیست تبدیل می‌شوند. سپس این لیست در خروجی چاپ می‌شود. در این خط، با فراخوانی متغیر gen، جنریتور تمام عناصر خود را یک به یک تولید کرده و با کمک تابع List() آن‌ها را به لیست تبدیل می‌کند.
  3. در عبارت print  دوم می‌خواهیم دوباره داده‌های درون متغیر gen  را به لیست تبدیل کنیم. اما از آن‌جا که Generator-ها فقط یک بار پیمایش شده و بعد از آن تمام می‌شوند، هیچ داده‌ای در متغیر gen  وجود ندارد. بنابراین در خروجی فقط لیست خالی چاپ می‌شود.

ارزیابی تنبل می‌تواند مشکل‌ساز شود

منظور از «ارزیابی تنبل» (Lazy Evaluation) آن است که Generator-ها فقط زمانی مقدار می‌دهند که توسط برنامه درخواست شود. در نتیجه، خطاهای موجود در داده‌ها یا تاثیرات جانبی مقادیر مختلف تا زمان استفاده واقعی از جنریتور و پیمایش آن معلوم نمی‌شوند.

استفاده بیش از حد از Generator

در زمان کار با مجموعه داده‌های کوچک یا اجرای پروژه‌های ساده، استفاده از Generator به خاطر صرفه‌جویی در حافظه، ارزش زیادی ندارد. زیرا زمان زیادی برای توقف و فعالیت دوباره Generator صرف می‌شود. در چنین موقعیت‌هایی میزان حافظه صرفه‌جویی شده خیلی کم بوده و لیست‌های معمولی به خوبی از پس انجام کار برمی‌آیند.

برای مثال، وقتی که باید بر روی داده‌های خاصی چندین بار مختلف پیمایش کنیم، بهتر است ابتدا داده‌ها را تولید کرده و در لیست مشخصی ذخیره کنیم. به‌جای آن که هر بار آن‌ها را با کمک Generator از ابتدا تولید کنیم.

فیلم های پروژه محور برای یادگیری پایتون در فرادرس

زبان پایتون، کاربردهای متنوعی مانند هوش مصنوعی، امنیت سایبری و غیره دارد. در نتیجه، افراد زیادی می‌خواهند با کمک فیلم‌های آموزشی فرادرس، پایتون را یاد بگیرند. آموزش همراه با اجرای پروژه‌های واقعی، یکی از روش‌های مؤثر برای یادگیری پایتون است. فرادرس فیلم‌های پروژه‌محور زیادی برای آموزش پایتون طراحی و منتشر کرده است.

در پایین، چند مورد از فیلم‌‌های آموزشی پروژه‌محور زبان پایتون را معرفی کرده‌ایم.

در صورت تمایل با کلیک بر روی تصویر زیر به صفحه اصلی این مجموعه آموزشی، هدایت شده و از فیلم‌های پروژه‌محور بیشتری دیدن کنید.

مجموعه آموزش پروژه محور برنامه نویسی پایتون (Python)
با کلیک بر روی تصویر بالا می‌توانید به صفحه اصلی مجموعه فیلم‌های آموزش پروژه محور برنامه نویسی پایتون هدایت شوید.

تشخیص زمان صحیح استفاده از Generator در پایتون

برای تشخیص زمان درست استفاده از Generator در پایتون باید به چند قانون ساده توجه کرد.

  • مجموعه داده‌های بزرگ:‌ در زمانی که تعداد داده‌ها برای ذخیره شدن در حافظه کامپیوتر خیلی زیاد باشد.
  • توالی‌های بی‌نهایت: وقتی با جریان‌های دائم داده مانند پخش زنده ویدئو یا شبیه‌سازی‌ها کار می‌کنیم.
  • «پایپ‌لاین‌ها» (Pipelines): وقتی داده‌ها باید مرحله‌به‌مرحله پردازش شوند، از Generator استفاده می‌کنیم. با آن‌ها می‌توانیم پایپ‌لاین‌های ماژولار بسازیم. در نتیجه داده‌ها را به مرور تغییر داده یا فیلتر می‌کنیم.

البته اگر شرایط زیر وجود داشت، بهتر است که در پایتون از لیست به‌جای Generator استفاده کنیم.

  • مجموعه داده‌های کوچک: وقتی که داده‌ها به راحتی در حافظه جای می‌گیرند و می‌خواهیم با سرعت به تمام آن‌ها دسترسی داشته باشیم.
  • چندین پیمایش: شاید بخواهید چند بار مختلف بر روی داده‌های یکسان پیمایش کنید. در چنین شرایطی بهتر است به‌جای Generator با لیست‌ها کار کنید. زیرا Generator هر بار بعد از پیمایش نابود می‌شود. برای استفاده دوباره از داده‌ها باز هم باید جنریتور را راه‌اندازی کنید.
زمان مناسب برای استفاده از Generatorزمان مناسب برای استفاده از لیست
کار با حجم انبوه دادهکار با مجموعه داده‌های کوچک
کا بر روی توالی‌های بی‌نهایتنیاز به چندبار تکرار پیمایش
ساخت پایپ‌لاین برای کار بر روی جریان دائم داده

جمع‌بندی

در این مطلب از مجله فرادرس با Generator در پایتون آشنا شدیم. Generator کمک بسیار زیادی در حل مسائل مربوط به دنیای واقعی مانند مدیریت مجموعه داده‌های بزرگ و ساخت پایپ‌لاین‌های زنده می‌کند. بهترین روش برای یادگیری کار با Generator-ها استفاده از این ابزار در برنامه‌‌های مختلف است. سعی کنید تعریف List Comprehension را به شکل عبارت Generator بنویسید. یا حلقه‌هایی که قبلا نوشته‌اید را به شکل توابع Generator بازنویسی کنید.

Generator کاربرد زیادی در حوزه علم داده دارد. بهترین محل استفاده از آن در زمان کار با مجموعه داده‌های خیلی بزرگ یا بی‌نهایت و زنده است. استفاده از Generator بر روی داده‌های کوچک یا داده‌هایی که باید چندین بار مختلف به آن‌ها رجوع کنیم چندان ایده مناسبی نیست. برنامه نویسان حرفه‌ای پایتون لازم است این ابزار را به خوبی بشناسند و بتوانند به راحتی با آن کار کنند.

بر اساس رای ۱ نفر
آیا این مطلب برای شما مفید بود؟
اگر پرسشی درباره این مطلب دارید، آن را با ما مطرح کنید.
منابع:
datacamp
PDF
مطالب مرتبط
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *