در این مقاله، آموزش بهینه سازی کدها در برنامه نویسی پایتون رو بصورت کامل برای شما همراهان گرامی قرار دادیم. در حال حاضر پایتون پراستفادهترین زبان برنامه نویسی است برای ساخت طیف گستردهای از پروژه های نرمافزاری مورد استفاده قرار گیرد. آمارها نشان میدهند طیف گستردهای از برنامه نویسان سراسر جهان از زبان برنامه نویسی پایتون برای توسعه برنامه های دسکتاپی و وب محور استفاده میکنند.
البته، این حرف به این معنا نیست که توسعه دهندگان پایتون از کدنویسی های نامرتب و غیرکارآمد که ممکن است زمان ارزشمند دیگر توسعه دهندگان هنگام بازبینی کدها را هدر دهند، مصون هستند. کدهایی که توسط پایتون یا دیگر زبانهای برنامه نویسی نوشته میشوند باید به دقت ارزیابی شوند تا کاستی ها و نارسایی های کدها برطرف شود. به بیان دقیقتر، برای آنکه پروژهها مورد توجه مشتریان و کارفرمایان قرار بگیرند، برنامه نویسان باید از تکنیکی که «بهینهسازی کدها» نام دارد، استفاده کنند.
قبل از آن که به بررسی مفهوم بهینه سازی کدها و روش انجام اینکار در پایتون اشاره کنیم، لازم است کمی در مورد طراحی کاربر-محور صحبت کنیم. طراحی کاربر-محور (User-Centered Design) یکی از گرایشهای مهم توسعه نرمافزارها است که چند سالی است مورد توجه تیم های نرم افزاری و شرکتها قرار گرفته است.
مزایای بالقوه طراحی مبتنی بر این گرایش کارکرد خود در صنایع مختلف را به خوبی نشان داده است. با توجه به اینکه، بخش عمدهای از توسعهدهندگان نرمافزار سال های متمادی است در این حوزه به فعالیت اشتغال دارند و برخی از آنها بر مبنای سبک و سیاق طراحی قدیمی کار میکنند، ممکن است در ابتدا روی خوش به طراحی کاربر-محور نشان ندهند.
طراحی کاربر- محور به مجموعهای از فرآیندهای تکرارشونده در طراحی اشاره دارد که در هر مرحله بر نیازسنجی درخواستهای کاربر متمرکز است تا راهحلی کارآمد در اختیار کاربران قرار دهد تا بتوانند از محصول توسعه یافته به بهترین شکل استفاده کنند. در طراحی کاربر-محور، انتخابهای طراحی بهشدت تحت تاثیر انتظارات، اهداف و خواستههای کاربر است.
در طراحی فوق، کاربران از ابتدا تا انتهای طراحی محصول در جریان پیشرفت کار قرار دارند. نکته مهمی که باید در این زمینه به آن دقت کنید این است که اصول طراحی کاربر-محور فراتر از یک طراحی ساده کاربری است و طراحان مجبور هستند محصولات را با هدف پاسخگویی به نیازهای طیف گستردهای از کاربران توسعه دهند. بهطور معمول، رویکرد فوق بهشکل تحقیقات کاربر، مصاحبه، تست کاربردپذیری و مجموعهای از بازخوردها انجام میشود. در بیشتر موارد، طراحی کاربر-محور مبتنی بر چهار مرحله زیر است :
هدف نهایی پارادایم طراحی کاربر-محور، توسعه محصولی برای کاربر و مواردی است که به آنها نیاز دارد، بهگونهای که تعامل با محصول بدون مشکل قابل انجام باشد. طراحی کاربر-محور به طراحان کمک میکند مشکلات کاربران را شناسایی کنند و به آنها اجازه میدهد از راهحلهایی در فرآیند توسعه استفاده کنند که برخواسته از نقطه نظرات کاربران است.
اجازه دهید، کار را با تعریف بهینه سازی کدها شروع کنیم تا ایده اولیهای بهدست آوریم و متوجه شویم که چرا نیازمند بهینه سازی کدهای پایتون هستیم. گاهی اوقات نوشتن کدهایی که قرار است کار خاصی انجام دهند، کافی نیست. کدهای بزرگ و ناکار آمد میتوانند سرعت اجرای برنامهها را کند کنند، ضررهای مالی به مشتریان وارد کنند یا در آینده، زمان ارزیابی و رفع مشکلات و باگها را بیشتر کنند. بهینه سازی کدهای پایتون راهکاری است که اجازه میدهد برنامهای با خطوط کمتر و میزان مصرف حافظه کمتر بنویسید و به این صورت، برنامهای کارآمدتر و روانتر در اختیار مشتری قرار دهید که نتایج دلخواه را در کمترین زمان ممکن در اختیارش قرار دهد.
زمانی که نوبت به پردازش تعداد زیادی عملیات یا دادهها میرسد، این مسئله اهمیت زیادی پیدا میکند. بنابراین، جایگزینی و بهینه سازی برخی بلوکها و ویژگیهایی که ممکن است روند اجرای یک برنامه را با مشکل روبهرو کنند، موضوع مهمی است که نباید بهسادگی از کنار آن عبور کنید. بهطور معمول، بهینه سازی کدها به دلایل زیر انجام میشود:
توسعه دهندگان پایتون باید به جای کدنویسی ساده از تکنیک های بهینه سازی کد استفاده می کنند تا اطمینان حاصل کنند برنامه ها به شکل سریع تری اجرا میشوند. در ادامه آموزش بهینه سازی کدها در برنامه نویسی پایتون با شش ترفند کاربردی در زمینه بهینه سازی کدها آشنا میشوید که کمک میکنند برنامه ها به شکل روان و سریعتری اجرا شوند.
برای درک بهتر تکنیک بهینه سازی Peephole، اجازه دهید کار را با نحوه اجرای کدهای پایتون آغاز کنیم. برای این منظور، ابتدا کدها را در یک فایل استاندارد بنویسید. در ادامه، دستور زیر را اجرا کنید
python -m compileall <filename>
در دستور بالا، بهجای filename باید نام فایل خود را تعیین کنید. پس از اجرا، همان فایل را با فرمت *.pyc که بیانگر کدهای بهینه است، دریافت خواهید کرد.
Peephole یک تکنیک بهینهسازی کدها در پایتون است که زمان کامپایل را بهبود بخشیده و عملکرد کدها را بهتر میکند. با استفاده از تکنیک بهینه سازی Peephole، کدها در پشت صحنه بهینه میشوند، پیش محاسباتی روی دستورات و عبارات ثابت یا آزمون های عضویت (Membership) روی آنها انجام میشوند. بهطور مثال، میتوانید چیزی شبیه تعداد ثانیه های یک روز را بهصورت a = 60*60*24 بنویسید تا کدها خواناتر شوند و مفسر زبان بهسرعت محاسبه ها را انجام دهد.
نتیجه تکنیک بهینه سازی Peephole در مورد مثال ثانیهها، این خواهد بود که پایتون عبارات ثابت 60*60*24 را از قبل محاسبه میکند و آنها را با 86400 جایگزین میکند. بنابراین، هنگامی که عبارتهای ریاضی فوق را مشاهده میکند، محاسبه ها را بهسرعت انجام میدهد و عملکرد برنامه کاربردی کاهش پیدا نمیکند.
چرا باید از تکنیک بهینهسازی Peephole استفاده کنیم؟
با استفاده از روش مذکور، میتوانید یک بخش از برنامه یا بخشی از دستورات را بدون اعمال تغییرهای قابل توجه در خروجی جایگزین کنید. به بیان دقیقتر، با استفاده از این روش بهینه سازی ساختارهای قابل تغییر را به غیرقابل تغییر تبدیل کنید. این کار را میتوان با استفاده از یکی از سه تاپل (Tuple) زیر انجام داد:
میتوانید، عضویت یک عنصر را با در نظر گرفتن دستورات به عنوان یک عمل با هزینه ثابت و بدون توجه به اندازه مجموعه، تایید کنید. مجموعه (set) و لیست (list) را به ثابتها تبدیل کنید.
نکته ای که باید به آن دقت کنید این است که فرآیند تبدیل تنها برای داده های خام توسط پایتون قابل انجام است. از اینرو، اگر مجموعه ها یا لیستهایی که استفاده میکنید فاقد داده های خام باشند، فرآیند بهینه سازی انجام نخواهد شد. برای روشن شدن بحث، اجازه دهید چند مثال ساده را بررسی کنیم:
Def peephole_func(): A= ”Hello, world” * 5 B= [1,2] *7 C=(10,20,30) * 3 Print(a,b,c)
عبارت ”Hello, world” * 5 یک دستور ثابت به طول کمتر از 4096 است. از اینرو، توسط کامپایلر بهصورت “Hello world” در 5 بار تکرار متوالی، استنتاج میشود.
عبارت [1,2] *7 یک لیست (شیء قابل تغییر) است، از اینرو، ارزیابی نمیشود.
عبارت (10,20,30) * 3 مجموعهای به طول 9 است که کمتر از 256 است (برای تاپلها)، از اینرو بهشکل (10, 20, 30, 10, 20, 30, 10, 20, 30) ذخیره میشود.
اشیاء رشتهای در زبان های برنامه نویسی مثل پایتون دنبالهای از کاراکترهای یونیکد هستند؛ از اینرو، در مستندات فنی، دنبالههای متنی (text) نامیده میشوند. هنگامی که کاراکترهایی با اندازههای مختلف به رشته ای اضافه میشوند، اندازه و وزن کل آن افزایش پیدا میکند، اما این افزایش اندازه فراتر از افزوده شدن چند کاراکتر است. پایتون اطلاعات اضافی را برای ذخیره رشتهها به آنها اختصاص میدهد که باعث میشود فضای زیادی از حافظه اصلی را مصرف کنند. پایتون، بهمنظور بهبود عملکرد راهکاری بهنام string interning در اختیار توسعه دهندگان قرار میدهد. string interning به این صورت عمل میکند که رشتههای خاص را هنگام ساخت، در حافظه کش میکند. به عبارت دقیقتر، تنها یک نمونه از یک رشته خاص در هر زمان مشخص، فعال است و هیچ حافظه جدیدی برای ارجاع به آن رزرو نمیشود.
String interning وجه شباهت زیادی با اشیاء مشترک (Shared Objects) دارد. هنگامی که یک رشته درونسازی (interned) میشود، بهعنوان یک شیء اشتراکی شناخته میشود، زیرا نمونهای از آن شیء رشته بهشکل سراسری توسط همه برنامههایی که در یک نشست پایتون اجرا میشوند بهاشتراک قرار میگیرند. در انشعابات پایتون مثل سایتون (CPython) هر زمان یک نشست تعاملی توسط پایتون مقداردهی اولیه میشود، اشیاء اشتراکی در حافظه بارگذاری میشوند. به همین دلیل است که string interning به پایتون اجازه میدهد بهشکل کارآمدی با رشتهها کار کند، بهطوری که زمان لازم برای پردازش کمتر شده و حافظه اصلی کمتری برای ذخیرهسازی رشتهها اشغال میشود.
رشته های identifier
معماری پایتون به این صورت است که ترجیح میدهد تنها رشتههایی را ذخیرهسازی کند که احتمال استفاده مجدد از آنها زیاد است. به بیان دقیقتر، به رشتههای شناسه (identifier) علاقه زیادی دارد. از جمله این رشتهها به موارد زیر باید اشاره کرد:
اصولی که بر مبنای آنها یک رشته درونسازی (Intern) میشود، بهشرح زیر است:
فقط رشته ای که در زمان کامپایل به عنوان یک رشته ثابت بارگذاری میشود، درونسازی میشود و برعکس، رشتهای که در زمان اجرا ساخته شده، درونسازی نخواهد شد. یک رشته اگر حاصل یک محاسبه constant folding باشد، بیان گر این موضوع است که عبارت های ثابت در زمان کامپایل بهجای زمان اجرا محاسبه شوند، اما اگر طولی بیشتر از 20 کاراکتر داشته باشند، درون سازی روی آن ها انجام نمیشود، زیرا تشخیص این مسئله که identifier هستند یا خیر، به سختی امکان پذیر است.
پایتون فقط در صورتی یک رشته را درونسازی و یک هش برای آن ایجاد میکند که رشته با نامی که ترکیبی از حروف، اعداد و یک حرف یا یک کاراکتر زیرخط دار است، تعریف شده باشد. از اینرو، تمام رشتههایی که از یک فایل خوانده میشوند یا از طریق شبکه دریافت میشوند، قابلیت درونسازی ندارند. البته، راهکاری برای حل این مشکل وجود دارد؛ کافی است چنین رشتههایی را با تابع ()intern بارگذاری کنید.
نمایه سازی کدها راهکار دیگری برای بهینه سازی کدها است. برای این منظور دو گزینه در دسترس توسعهدهندگان قرار دارد :
استفاده از <timeit> در برنامه نویسی پایتون
این ماژول مدت زمان مورد نیاز برای اجرای یک وظیفه توسط یک بلوک خاص از کدها را بر حسب میلی ثانیه محاسبه میکند. نحوه فراخوانی و اجرای ماژول فوق در تصویر 1 نشان داده شده است. خروجی ماوژول فوق نیز در تصویر 2 نشان داده شده است.
استفاده از <cProfile> در برنامه نویسی پایتون
cProfile یک ماژول پیشرفته و بخشی از بسته نرم افزاری است که اولین بار همراه با پایتون 2.5 در دسترس توسعه دهندگان قرار گرفت. توسعه دهندگان میتوانند به روشهای زیر این ماژول را به کدهای پایتون اضافه کنند:
بهطور مثال، میتوانید از ترکیب نحوی زیر استفاده کنید:
python -m cProfile code.py arg1 arg2 arg3
با دانستن عناصر کلیدی گزارش cProfile، میتوانید گلوگاه های موجود در کدهای خود را پیدا کنید. نکته مهمی که باید به آن دقت کنید این است که تکنیک فوق تنها با فایل های پایتون کار میکند، از اینرو، نمیتوانید یک CLI پایتون را با فراخوانی مستقیم آن بهشکل زیر نمایه کنید :
python -m cProfile my_cli
اگر در نظر دارید یک CLI را پروفایل کنید، باید مسیر ورودی را در اختیار cProfile قرار دهید. ترکیب نحوی، چیزی شبیه به حالت زیر خواهد بود:
python -m cProfile venv/bin/my_cli
با اجرای دستور بالا، پس از اتمام برنامه، یک جدول خروجی مانند تصویر زیر خواهید داشت
عناصر موجود در تصویر بالا به شرح زیر هستند :
از راهکاری های قدرتمند دیگری که برای بهینه سازی حافظه در اختیار توسعه دهندگان قرار دارد، ژنراتورها و کلیدها هستند. نکته ای که باید در مورد ژنراتورها به آن دقت کنید این است که همه آیتمها (تکرارگرها) را به یک باره برنمیگردانند، بلکه میتوانند هر بار فقط یک مورد را برگردانند. هنگام مرتب کردن عناصر در یک لیست، بهتر است از کلیدها و روش پیشفرض <sort()> استفاده کنید. بهطور مثال، میتوانید فهرستها و رشتهها را بر اساس شاخص انتخابشده به عنوان بخشی از آرگومان کلید مرتب کنید. تصویر زیر این موضوع را نشان میدهد.
هزاران عملگر و کتابخانه داخلی در پایتون موجود است. بهتر است تا جایی که ممکن است از ویژگیهای داخلی و ازپیش تعریف شده پایتون استفاده کنید تا عملکرد کدها افزایش پیدا کند. عملگرها و ماژول هایی که از پیش ساخته شدهاند از قبل کامپایل شدهاند و همین مسئله سرعت اجرای برنامه های کاربردی را بیشتر میکند. برخی از کتابخانه های پایتون، ویژگی ها و قابلیت های ارزشمندی ارائه میکنند که سرعت و عملکرد برنامه های کاربردی را به میزان قابل توجهی افزایش میدهند.
به طور مثال، کافی است به جای Pickle از cPickle استفاده کنید تا تفاوت ها را مشاهده کنید. بسته های PyPy و <Cython> راهی برای بهینه سازی یک کامپایلر ایستا برای سریع تر کردن فرآیند پردازش ها ارائه میکنند.
Global ها میتوانند عوارض جانبی آشکار و پنهان زیادی داشته باشند که در نهایت کدنویسی شما را به سبکی که «کدهای اسپاگتی» نام دارند، سوق میدهند. همچنین، پایتون در دسترسی به متغیرهای خارجی کند است. بنابراین، بهتر است از آنها استفاده نکنید یا دستکم استفاده از آنها را محدود کنید. اگر استفاده از Global اجتنابناپذیر است و مجبور هستید از آنها استفاده کنید به دو مورد زیر دقت کنید :
به عنوان صحبت پایانی : ساخت یک برنامه قدرتمند و مقیاس پذیر که بتواند وظایف محوله را بهسرعت انجام دهد و در عین حال از حافظه اصلی بهشکل بهینه ای استفاده کند، مهم است. با این حال، ساخت چنین برنامهای تنها بر پایه کد نویسی های پایه غیرممکن است. به همین دلیل است که باید کدهای پایتون را بهینهسازی کنید. اگر به روشهای بهینه سازی که در این مقاله شرح داده شد دقت کنید، نهتنها کد نویسی تمیزی انجام خواهید داد، بلکه عملکرد برنامههای کاربردیتان بهبود پیدا خواهد کرد و روند بازبینی کدها در آینده کوتاهتر خواهد شد.