یک‌بار برای همیشه - ‌Comprehension‌ها

مقدمهسلام، امیدوارم که مثل همیشه خوش و خرم باشید. این‌بار می‌خوایم بریم سراغ یکی از ویژگی‌های خیلی خوب پایتون که شاید خیلی بهش‌ توجه نشده باشه. از طرفی دیگه ممکن هست که دید خوبی به این ویژگی‌ نداشته باشید. توی این قسمت از یک‌بار برای همیشه تصمیم گرفتیم یک بررسی کوتاه اما جامع روی comprehensionها توی پایتون داشته باشیم. یک نکته‌ای که دوست دارم توی ذهن‌تون ماندگار بشه این هست که comprehensionها ابزاری برای کم کردن تعداد خط‌های کد نیستند!دوباره، ‌comprehensionها ابزاری برای کم کردن تعداد خط‌های کد نیستند!!!معرفیمثل همیشه اولین گام یکی کردن ادبیات‌مون هست:اصطلاح اول iterable: توی قسمت iteratorها این مفهوم رو به صورت مفصل توضیح دادیم. هر چیزی که به وسیله‌ی for loop هر بار یک آیتم از اون data type بگیریم iterable هست. مثل list، فایل‌ها، set و ... .اصطلاح list comprehension: به ما امکان ساخت یک لیست رو از یک داده‌ی iterable میده.اصطلاح generator: توی قسمت iteratorها این مفهوم رو به صورت مفصل توضیح دادیم پس اگر حس می‌کنید با این مطلب آشنا نیستید توصیه می‌کنم اول این قسمت رو یک مطالعه مختصر کنید. یک iterable تنبل رو generator می‌گیم.اصطلاح generator expression: با کمک این مفهوم ما یک generator می‌سازیم.اگر هر کدوم از این اصطلاحات کامل براتون جا نیفتاده، نگران نباشیم جلوتر همه رو به صورت عملی می‌بینیم!در پایتون هر متغییر در حقیقت یک ارجاع(رفرنس) به یک آبجکت‌ هست. در نتیجه توی حالت زیر وقتی سعی می‌کنیم که از لیست‌ای که داریم یک دونه کپی کنیم، هیچ کپی کامل‌ای رخ نمیده فقط دو اشاره‌گر به یک آبجکت به ما میده.list copy problemاگر تازه وارد پایتون شده باشید ممکنِ این رفتار یکم واسه شما عجیب بنظر برسه. به همین خاطر ساختن یک لیست از روی لیست دیگه توی پایتون خیلی کارِ رایجی هست. در حقیقت میشه گفت دلیل فلسفی وجودlist comprehensionها بهمین دلیل هست!توی مثال زیر ما یک لیست داریم و هدف‌مون محاسبه مجذور اعداد زوج هست:traditional wayهمین کار رو اگر بخوایم با لیست comprehension انجام بدیم این شکلی میشه:list comprehensionهمان‌طور که می‌بینید توی مثال بالا خیلی از اطلاعات به صورت فشرده توی یک خط نوشته شده، برنامه اول چهار خط کد داشت ولی اینجا همه‌چیز با یک خط انجام شده، اما بریم یکم بیشتر این ساختار رو بررسی کنیم. من سعی کردم با رنگی کردن هر بخش، بخش متناظر با اون رو توی لیست comprehension نشون بدم( اگر ابزار خوبی برای این کار رو می‌شناسید ممنون میشم به منم معرفی کنید):پس تقریباً ما می‌تونیم خیلی از for-loopها رو به راحتی تبدیل کنیم به list comprehension فقط کافیه ساختار بالا توی ذهن ما باشه. توی گیفِ پایین این‌‌کار به صورت عملی نشون داده شده:Copied from https://treyhunner.com آیا واقعا لیست comprehension خوانایی کد رو کم می‌کنه؟به نظر من که خیر، چون وقتی توی پایتون شما یک قطعه کدی رو داخل ()، ‌[] یا {} بذارید، شما می‌تونید به هر حالتی که دوست دارید فاصله یا newline به کدتون اضافه کنید و خوانایی کد رو بالا ببرید. این ویژگی به ما کمک می‌کنه تا بتونیم خوانایی کد رو در حالت comprehension تا حد خیلی خوبی افزایش بدیم.make it more readableپس هیچ‌وقت نیاز به استفاده از \ برای اضافه کردن newline نداریم، مثال پایین غلط هست، هیچ‌وقت اینجوری ننویسید!wrong way!پس تا اینجای کار باید متوجه شده باشید که comprehensionها برای کم کردن تعداد خط‌های برنامه نیستند! comprehensionها راه دومی برای انجام کار قبلی هستند که فقط کد ما رو گویاتر و خواناتر می‌کنه، توی comprehension انگیزه توسعه‌دهنده در رابطه با کارش‌ محسوس‌تر هست.لازم نیست همیشه یک شرط داخل comprehension ما باشه. شرط‌ها فقط به ما کمک می‌کنند که یک فیلتر توی روند اجرای comprehension ایجاد کنیم.ما می‌تونیم یک comprehension تو در تو هم داشته باشیم مثل مثال بالا که به صورت غلط از \ برای جدا کردن خطوط داخل comprehension استفاده کرده بود. مثال:better way!نکته، comprehension‌ها فقط محدود به لیست نیستند و ما set comprehension هم داریم که مشابه حالت لیست برای ساخت یک set جدید به کار میرن، مثال:set comprehensionما dictionary comprehension هم داریم که خیلی از حالت‌ set پر کاربرد‌تر هست، توی مثال پایین ما خواستیم جای key-value روی توی دیکشنری‌مون عوض کنیم:dictionary comprehensionما چیزی به اسم tuple comprehension نداریم، چون generatorها خیلی توی پایتون کاربردی هستند، ما می‌تونیم یک generator رو توسط comprehensionها بسازیم اما دیگه بهش generator compehension گفته نیمشه بلکه اون رو با اسم generator expression می‌شناسیم.اگر با generatorها آشنا باشید پس میدونید که generatorها یک‌بار مصرف هستند یعنی اگر یک‌بار روی اون‌ها iterate کنیم دیگه امکان نداره به ایتم‌های اون بتونیم دوباره دست‌پیدا کنیم این در حقیقت با تعریف generatorها برابر هست!همین ما رو به سمت این نکته سوق می‌ده که هر موقع دیدید دارید یک list comprehension ای می‌سازید که فقط یکبار از اون استفاده می‌کنید بجاش از generator expression استفاده کنید! چرا؟ چون فلسفه‌ی وجودی این دوست‌مون برای همین کار هست!اما چطور یه دونه از اونا بسازیم، مثال(توجه کنید که فقط [] به () تغییر کرده):Generator expressionفقط یکبار استفاده ممکن هست در ابتدا خیلی ملموس نباشه، هر موقع می‌گیم یکبار استفاده، یعنی اون متغییری که ساختیم هیچ‌جای دیگه غیر از اونجا به کار ما نمیاد، در نتیجه نیازی نیست جوری ساخته بشه انگاه می‌خواهیم چندبار از اون استفاده کنیم. مثال خیلی واقعی‌ترش به صورت پایین میشه که یک مثال رو توی سه مدل با هم بررسی کنیم:Generator expressionکی نباید از comprehensionها نباید استفاده کرد:هر موقع دارید یک comprehensionای می‌سازید که نه چیزی رو فیلتر می‌کنه نه operationای روی داده‌ها انجام میده، اونجا احتمالاً دارید به صورت غلط از comprehension استفاده می‌کنید، مثال:مثال غلط دیگه می‌تونه ساخت لیست جدید از روی لیست قدیمی با کمک comprehension باشه، چرا می‌گیم غلط هست (پایتونیک نیست) چون تابع سازنده list برای همین ساخته شده:جمع‌بندییادمون نره comprehension برای کم کردن خط‌های کد نیستند!هر موقع دیدید دارید یک list comprehension یک‌بار مصرف می‌سازید، حتماً ازgenerator expression استفاده کنید.با استفاده از newline و در نظر داشتن PEP8 تا جایی که میشه comprehension‌ها رو خواناتر کنیم.اگر دنبال مثال برای تمرین می‌گردید این وب سایت می‌تونه کمک خیلی خوبی به شما بکنه: لینک.در پایان اگر جای خیلی باحالی از comprehensionها استفاده کردید خیلی ممنون می شیم اونارو هم با ما به اشتراک بذارید. اگر از این مطلب‌ خوشتون اومده اون رو توی شبکه‌های اجتمالی با دو‌ستان و همکار‌ خودتون به اشتراک بذارید. تا قسمت بعدی، خدانگهدار!