بهبود خوانایی کدهای پایتون | راهنمای گام به گام


امروزه زبان برنامهنویسی پایتون کاربردهای بسیار گسترده و متنوعی یافته است. اینک کد پایتون را در ضمیمه مقالات علمی، دیتاستهای منتشر شده یا در رقابتهای برنامهنویسی از قبیل kaggle مشاهده میکنیم. البته انتشار کد از جهت این که به بازتولید نتایج کمک میکند کار پسندیدهای است و وجود کد از نبود آن بهتر است، اما در اغلب موارد خوانایی این کدها باید افزایش یابد. در این مقاله با روش بهبود ظاهر خوانایی پایتون آشنا خواهیم شد.
توانایی کدنویسی در دنیای مدرن به اندازه توانایی خواندن و نوشتن اهمیت یافته است. بهترین روش برای تبدیل شدن به یک برنامهنویس بهتر این است که در یک شرکت با استانداردهای کدنویسی بالا مشغول به کار شویم. در این راهنما در مورد موقعیتهایی صحبت خواهیم کرد که شما به تنهایی روی یک کدبیس کار میکنید. مخاطب این راهنما، محققان، دانشمندان داده و توسعهدهندگان تازهکار نرمافزار است. تبدیل شدن به یک برنامهنویس بهتر، یک فرایند پیوسته است. هر چه کد بیشتری بنویسید، به کدنویس بهتری تبدیل میشوید.
ما در این مقاله قصد نداریم در خصوص شیوه تبدیل شدن به یک برنامهنویس خوب صحبت کنیم، بلکه صرفاً برخی نکات و ترفندها را برای افزایش خوانایی کد معرفی میکنیم. به این ترتیب با مطالعه این راهنما در طی 9 گام با روش بهبود خوانایی کد آشنا خواهید شد.
از کنترل نسخه برای ردگیری تغییرهای کد استفاده کنید
شاید فکر کنید نسخهبندی کد یک الزام بدیهی است، اما بسیاری از افراد همین نکته بدیهی را رعایت نمیکنند. برخی از محققان نسخههای مختلف کد و فرایند رفع باگ را حتی از طریق تبادل ایمیل بین اعضای تیم انجام میدهند. نسخهبندی کد از طریق ریپازیتوریهای مرکزی در گیتهاب یا دیگر سرویسها یک الزام است. محققان زیادی این کار را انجام میدهند، اما اگر از علوم رایانه جدا شوید، اغلب اساتید و دانشجویان هنوز از روش نسخهبندی با استفاده از پوشهها و بهکارگیری ایمیل بهره میگیرند.
شما از نخستین خط کدی که مینویسید، باید از گیت استفاده کنید. به این منظور یک ریپازیتوری روی گیتهاب ایجاد کنید و کد را آنجا بفرستید. اگر فکر میکنید که کد نباید مورد دسترس عموم باشد، میتوانید یک ریپازیتوری خصوصی ایجاد کنید. هر چند این امکان برای ایرانیان محدود شده است و برای بهرهگیری از ریپازیتوریهای خصوصی باید این تحریمها را به شیوهای دور بزنید.
برخی افراد این ایده را در ذهن خود دارند که کد باید هیچ نقصی نداشته باشد تا به صورت عمومی منتشر شود. این افراد بر این باورند که با انتشار کدهای غیر کامل، ممکن است افراد دیگر آنها را برنامهنویسان بدی تلقی کنند. واقعیت این است که هیچ کس بر اساس یک کد در مورد کیفیت برنامهنویسی دیگران قضاوت نمیکند و علاوه بر آن هر کدی که امروز مینویسید، شش ماه دیگر که به آن نگاه کنید، از نظر خودتان هم کد بدی خواهد بود!
با این حال وجود یک ریپازیتوری خصوصی، بهتر از عدم وجود ریپازیتوری است. هر چند ریپازیتوری عمومی بهتر از ریپازیتوری خصوصی است. همچنین بهتر است زمانی که در رقابتهای Kaggle شرکت میکنید، pipeline-های خود را پس از اتمام مسابقه منتشر کنید. این یک عادت خوب است که به خود برنامهنویسان و همچنین جامعه برنامهنویسی کمک میکند. یک ابزار به نام Sourcegraph (+) وجود دارد که امکان اجرای جستجو در کد را در میان همه ریپازیتوریهای گیتهاب فراهم میسازد. استفاده از این ابزار بسیار راحت است و موجب افزایش بهرهوری شما میشود.
کد را به شاخه master نفرستید
زمانی که مشغول کار در یک تیم هستید، باید کد را به صورت مستقیم به شاخه master ارسال کنید. در این موارد باید یک شاخه مجزا ایجاد کرده و روی آن کار کنید. یک درخواست pull (PR) ایجاد کنید و سپس این درخواست Pull را در شاخه مستر ادغام (merge) کنید. دلیل این وضعیت آن است که برای ادغام کدها در شاخه مستر، تغییرهای کد باید از وجوه مختلف مورد بررسی قرار گیرند. بررسی دستی کد از سوی همکاران به نام «مرور کد» (code review) شناخته میشود. بررسیهای خودکار شامل نحو، استایل، تستها و غیره نیز باید انجام یابند. البته زمانی که به تنهایی کار میکنید، امکان مرور کد از سوی همکاران وجود ندارد، اما همچنان میتوانید از قابلیت بررسیهای خودکار بهره بگیرید.
بهتر است تنظیمات گیتهاب را طوری قرار دهید که حتی در صورت تلاش نیز نتوانید کد را به شاخه مستر ارسال کنید. همچنان که پیشتر اشاره کردیم، این کار از بروز اشکالها جلوگیری کرده و در انرژی شما صرفهجویی میکند.
از سیستمهای CI/CD استفاده کنید
به طور کلی طی فرایند ایجاد شاخهها و ادغام کردن آنها موجب ایجاد سربار میشود. دلیل اصلی این است که امکان اجرای بررسیها را روی تغییرهای کد فراهم میسازد. پس از این که سیستم ادغام پیوسته را راهاندازی کردید، هر درخواست pull تنها پس از آن که همه بررسیها پاس شد، ادغام میشود.
سرویسهای زیادی وجود دارند که کارکرد CI/CD را عرضه میکنند. از جمله آنها GitHub Actions، CircleCi، Travis ،Buildkite و غیره هستند. ما استفاده از GitHub Actions را پیشنهاد میکنیم. این سرویس رایگان است و روی هر دو نوع ریپازیتوریهای خصوصی و عمومی و راهاندازی آسانی دارد. همه این موارد ممکن است پیچیده به نظر برسند، اما در عمل باید یک فایل پیکربندی به ریپازیتوری اضافه کنید.
ابزارهای قالببندی کد
روشهای مختلفی برای قالببندی یک قطعه کد وجود دارد. به این منظور باید به سؤالات زیر پاسخ دهیم:
- بین تابعها چند فاصله باید وجود داشته باشد؟
- خطوط کد چه طولی باید داشته باشند؟
- ترتیببندی ایمپورتها چگونه است؟
- از چه نوع گیومهای برای تعریف یک رشته استفاده کرد؟
- و سؤالات بسیار دیگر.
ابزارهایی به نام Code Formatter وجود دارند که روی کد اجرا میشوند و کد را طوری تغییر میدهند که با الزامات تعریف شده هماهنگی پیدا کنند. از جمله فرمترهای عمومی به شرح زیر هستند:
- YAPF (+) – بسیار انعطافپذیراست. شما میتوانید آن را طوری پیکربندی کنید که با استایل مورد نظرتان تطبیق پیدا کند.
- Black (+) - انعطافپذیر نیست و تنها طول خط را میتوان پیکربندی کرد.
یکی از این فرمترهای فوق را انتخاب کرده و به پیکربندی CI/CD خود اضافه کنید. استفاده از این فرمترها موجب میشود که همه پروژههای شما ظاهر یکسانی پیدا کنند. فرمترهای کد بحث «سوئیچ زمینه» (Context Switching) را کاهش میدهد و از این رو خوانایی کد افزایش مییابد. برخی فرمترهای خاصتر نیز وجود دارند. برای نمونه isort (+) صرفاً ایمپورتها را مرتبسازی میکند.
فرمترها باید در دو محل اجرا شوند:
در CI/CD – فرمترها در این محل به منظور بررسی استفاده میشوند و فرمتر اعلام میکند که آیا فایلهایی وجود دارند که باید فرمت شوند یا نه. با این حال کد هیچ تغییری نمییابد.
محل دیگری که فرمتر استفاده میشود، به طور لوکال پیش از کامیت کردن تغییرها است.
قلاب پیش از کامیت
در گام قبلی در مورد میزان اهمیت اجرای فرمترها به صورت لوکال و پیش از کامیت کردن صحبت کردیم. فرض کنید از دو فرمتر Black و isort استفاده میکنیم. در این زمان باید دستورهای زیر را اجرا کنیم:
- .black
- isort
این وضعیت ملالآور است. یک راهحل بهتر این است که یک اسکریپت bash با دستورهای فوق بسازیم. میتوانیم نام آن را برای مثال code_formatter.sh بگذاریم و آن را اجرا کنیم. ایجاد چنین اسکریپتهایی یک رویکرد رایج است، اما مشکل اینجا است که در این حالت افراد آن را اجرا نمیکنند، مگر این که مجبور باشند.
یک راهحل بهتر این است که یک «قلاب پیش از کامیت» (pre-commit hook) داشته باشیم. ایده کار مشابه اسکریپت bash است، اما مواردی که میخواهیم پیش از هر کامیت اجرا شوند، به طور دقیق در این زمان اجرا خواهند شد:
git commit -m “<commit message>”
گرچه این یک تغییر کوچک به نظر میرسد، اما در عمل چنین نیست. در زمان استفاده از pre-commit، رفتار شما چنان که اشاره کردیم الزام شده است و کارکرد بهتری دارد.
با این که PyCharm قالببندی مناسبی را اجرا میکند، اما با این حال بهتر است از pre-commit استفاده کنید، زیرا ممکن است فراموش کنید از PyCharm برای قالببندی کد استفاده کنید. به علاوه زمانی که دو یا چند نفر در تیم مشغول کار باشند، باید مطمئن شوید که قالببندی کد آنها یکسان است. در زمان استفاده از قلاب pre-commit، همه افراد پیکربندی یکسانی را به عنوان بخشی از ریپازیتوری خود خواهند داشت. اما شاید گزینه بهتر آن باشد که هردوی آنها را همزمان استفاده کنید، یعنی یک قلاب pre-commit داشته باشد و کد را با PyCharm قالببندی کنید.
این امکان وجود دارد که کامیتها را از کنسول اجرا کنید، اما آن را به طور مستقیم از PyCharm انجام دهید. شما میتوانید PyCharm را طوری پیکربندی کنید که قلاب pre-commit را روی هر کامیت اجرا کند.
از Linter-ها استفاده کنید
لینتر به ابزاری گفته میشود که تغییری در کد ایجاد نمیکند، اما آن را از نظر وجود مشکلات احتمالی مورد بررسی قرار میدهد. رایجترین نوع این ابزارها flake8 (+) نام دارد.
لینترها میتوانند موارد زیر را مورد بررسی قرار دهند:
- خطاها و هشدارهای pep8.
- قواعد یکنواخت برای نامگذاری.
- پیچیدگی چرخهای تابعها.
- همچنین امکان بررسی موارد دیگر از طریق برخی پلاگینها وجود دارد.
Flake8 یک ابزار قدرتمند است و بهتر است آن را علاوه بر قلابهای پیش از کامیت به پیکربندی CI/CD خود اضافه کنید.
Mypy: بررسی نوع استاتیک
از پایتون نسخه 3 به بعد، امکان افزودن حاشیهنویسی (annotations) به تابعها فراهم آمده است. استفاده از این قابلیت اختیاری است، اما قویاً توصیه شده است. یکی از دلایل این مسئله آن است که موجب میشود خواندن کد در زمان وجود حاشیهنویسیهای نوع بسیار آسانتر شود. برای نمونه وقتی کدی مانند زیر میبینیم:
x: pd.DataFrame
بسیار آگاهیبخشتر از کد زیر است:
x
البته بدیهی است که کلاً نباید از یک نام متغیر مانند x استفاده کرد، اما با این حال در این راهنما در مورد روشهای ساده و خودکار بهبود کد صحبت میکنیم و مبحث نامگذاری خوب کمی دشوارتر از ساده است!
چنان که اشاره کردیم استفاده از حاشیهنویسی، امری اختیاری است. کد بدون وجود حاشیهنویسی نیز به درستی کار میکند. با این حال ابزاری به نام Mypy وجود دارد که موارد زیر را بررسی میکند:
- حاشیهنویسی نوع برای تابعها و پارامترهای ورودی.
- انسجام بین نوع متغیر و عملیات روی آنها.
این یک ابزار بسیار مفید است. همه شرکتهای بزرگ از این ابزار برای بررسی درخواستهای pull کد پایتون بهره میگیرند.
Mypy شما را ملزم میکند کدی بنویسید که خوانایی آسانتری داشته باشد، تابعهای بیش از حد پیچیده را بازنویسی کنید، تابعهای با نگارش بد را از نو بنویسید و باگها را به سهولت شناسایی کنید.
بررسیهای Mypy باید به CI/CD و قلاب pre-commit اضافه کنید.
بررسیهای بیشتر با قلاب pre-commit
امکان بسط قلاب pre-commit با موارد مختلف وجود دارد.
- حذف فضاهای خالی انتهایی.
- انتهای فایل در خط جدید.
- فایل الزامات مرتبسازی.
- بررسی فایلهای yaml برای وجود قالب صحیح.
- و غیره.
امکان ایجاد قلابهای سفارشی برای قالببندی کد و اجرای خودکار بررسیها وجود دارد.
ابزارهای خارجی
یک حوزه توسعه فعال برای ابزارهایی وجود دارد که از «یادگیری ماشین» (ML) برای تحلیل کد روی درخواستهای pull استفاده میکنند. همه آنها برای ریپازیتوریهای عمومی رایگان هستند و برخی از آنها برای ریپازیتوریهای خصوصی نیز رایگان هستند. استفاده از این ابزارها روی ریپازیتوریهای عمومی بسیار مفید است.
سخن پایانی درباره بهبود خوانایی کدهای پایتون
در این نوشته با برخی ترفندها برای بهبود ظاهر کدهای پایتون آشنا شدیم.
اگر فردی دست کم برخی از این تکنیکها را پیادهسازی کند، کد او خوانایی خوبی پیدا میکند. اما این نخستین گام است. برخی تکنیکهای استاندارد دیگر نیز به شرح زیر وجود دارند:
- تستهای unit
- تستهای unit با فرضیه
- محیطهای پایتون
- پکیجهای build کردن
- داکرسازی
- مستندسازی خودکار
با این حال مواردی که در این نوشته مطرح شدند، پیادهسازی آسانی دارند. امیدواریم با مطالعه این نوشته با روشهای بهبود خوانایی کدهای پایتون آشنا شده باشید.