0% found this document useful (0 votes)
118 views

Python Tricks Boby - Cloud

Uploaded by

Ali Zadehkafi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
118 views

Python Tricks Boby - Cloud

Uploaded by

Ali Zadehkafi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 294

‫خوانندهی محترم‪ ،‬از خرید و حسن اعتماد شما سپاسگزاریم‪.

‬‬
‫لطفا برا ی حمایت از حفظ حقوق نویسندگان و مصنفان از هرگونه نشرر و پپرغ رمرمزراز ایرن ا رر‬
‫خودداری پنمد‪ .‬حمایت شما موجب تولمد محتوای با پمفمت توسط جامعهی نویسندگان خواهد شد‪.‬‬
‫در صورتغ په این پتاب از روشهای دیگرری رمرر از وبسرایت رسرمغ متررجممن بره دسرت شرما‬
‫رسمده است‪ ،‬لطفا اقدام به خرید و مطالعهی نسخه قانونغ خود پنمد‪.‬‬

‫‪https://boby.cloud‬‬ ‫‪https://devmo.in‬‬

‫اممدواریم از مطالعهی پتاب به همراه یک فنزان قهوه داغ لذت ببرید‪.‬‬


‫ترفندهای پایتون‬

‫ترفندهای اپیتون‬

‫مترجمین‪:‬‬
‫سید محمد بابازاده‬
‫سید معین باباپور‬
‫ناشر‪ :‬پژوهندگان راه دانش‬
‫شماره نشر‪1499 :‬‬
‫نام کتاب‪ :‬ترفندهای پایتون‬
‫مترجمین‪ :‬سیدمحمد بابازاده ‪ -‬سید معین باباپور‬
‫طراح جلد‪ :‬امیرمحمد حسینی پور‬
‫تیراژ‪1000 :‬‬
‫نوبت چاپ‪ :‬اول ‪1399‬‬
‫چاپ‪ :‬ترنگ‬
‫صفحهآرایی‪ :‬پژوهندگان‬
‫سایز‪ :‬وزیری‬
‫شابک‪978-600-345-496-5 :‬‬
‫قیمت‪ 150000 :‬تومان‬

‫حق چاپ و نشر محفوظ است‪.‬‬


‫طبق قانون حقوق نویسندگان و مصنفان هرگونه کپیبرداری به هر روش پیگرد قانونی دارد‪.‬‬

‫دفتر اهواز‪09161185118 - 32202802 :‬‬ ‫دفتر تهران‪09128884969 - 66120554 :‬‬


‫پایتونیستها راجع به کتاب «ترفندهای پایتون» چه میگویند؟‬

‫من عاشق این پتاب هستم‪ .‬این پتاب همانند یک آموزش طبقه بندی شده ترفندهای‬
‫جالب پایتون است‪ .‬من در حال یادگمری پرایتون هسرتم و برا پرمی زممنره ‪Powershell‬‬
‫وارد پایتون شدم‪ .‬معموال زمانغ په در نوشتن یک قطعه پد پرایتون گمرر مرغپنم‪ ،‬یرک‬
‫پست در چت روم داخلغ پایتون ارسال مغپنم و سؤالم را مغپرسم‪.‬‬
‫من ارلرب از پاسر هایغ پره از همارارانم دریافرت مرغپنم‪ ،‬شرگفت زده مغشروم‪.‬‬
‫ترفندهایغ در دیاشنریها‪ ،‬المبداها‪ ،‬ژنراتورها و ‪ ...‬همواره به عنروان چاشرنغ در پاسر‬
‫دوستانم است‪ .‬من هممشه تحت تا مر قرار مغگرفتم و در عمن حال شگفت زده مغشردم‬
‫په پایتون چقدر قدرتمند خواهد بود اگر این ترفندها را بشناسرم و آنهرا را بره درسرتغ‬
‫پمادهسازی پنم‪.‬‬
‫این پتاب دقمقا همان پترابغ اسرت پره بره دنبرال آن مغگشرتم‪ .‬مرن برا پرمی زممنره‬
‫‪ Powershell‬وارد پایتون شدم و نماز به شخصغ داشتم تا از او بپرسم چه زمانغ بایرد از‬
‫ترفندهایغ په جامعه پایتون از آن صحبت مغپند‪ ،‬استفاده پنم‪.‬‬
‫به عنوان پسغ په مدرک مربط با علوم پامپموتر ندارد‪ ،‬به یک راهنمرای متنرغ نمراز‬
‫داشتم تا مواردی را توضمح دهد په افراد دیگر در پالسهای دانشگاه علروم مررتبط برا‬
‫پامپموتر مطالعه پردهاند‪ .‬من از خواندن این پتاب لذت زیادی بردم‪.‬‬

‫‪Daniel Meyer‬‬
‫مدیر ارشد شرپت ‪Tesla‬‬
‫برای اولمن بار از هماارم در مورد این پتاب شنمدم‪ .‬او مغخواست من را با مثالهای‬
‫نحوه ساخت دیاشنریهای پایتون در این پتاب فریب دهد‪ .‬مرن حردس مرغزدم نتمزره‬
‫باید یک دیاشنری پوچاتر باشد اما باید اعتراف پنم په انتظار نتمزه را نداشتم‪.‬‬
‫همان بعد از ظهر یک نسخه از این پتاب خریدم و شررو بره مطالعره پتراب پرردم‪.‬‬
‫روز بع د برای نوشمدن یک فنزان قهوه یاغ از همارارانم را مالقرات پرردم و از همران‬
‫ترفند برای او استفاده پردم!‬
‫او شرو به پرسمدن سؤالهای متفاوتغ پرد اما به دلمل ایناه مطالب در این پتاب به‬
‫خوبغ توضمح داده شده است‪ ،‬نه تنها به راحتغ جواب را حدس زدم بلاه به درستغ نمرز‬
‫به هماارم توضمح دادم‪ .‬این یعنغ پار بزرگغ در توضمح ترفنردهای پتراب انزرام شرده‬
‫است‪.‬‬
‫من در پایتون تازه پار نمستم و بعضغ از ترفندهای گفته شده در این پتاب برای مرن‬
‫جدید نبودند اما باید بگویم په از هر فصل مطالب جدیدی را یاد گرفتم‪ .‬بابت خوانردن‬
‫این پتاب خوب‪ ،‬خوشحالم و سپاسگزارم په پشرت صرحنه ترفنردها بره صرورت پامرل‬
‫توضمح داده شده است‪ .‬من یقمنا به همااران و دوستانم این پتاب را معرفغ خواهم پرد‪.‬‬

‫‪Og Maciel‬‬
‫توسعه دهنده پایتون شرپت ‪Red Hat‬‬
‫من از خواندن پتاب دن لذت زیادی بردم‪ .‬او مباحث مهم پایتون را با مثالهرایغ واضرح‬
‫توضمح مغدهد‪( .‬بررای مثرال گربرههای دوقلرو بررای توضرمح تفراوت برمن == و ‪ is‬در‬
‫پایتون)‬
‫این پتاب فقط نمونه پد نمست‪ .‬بلاه در رابطه با جزئمات پمادهسازی به صورت قابل‬
‫مالحظهای بحث شده است‪ .‬هرچند تنها چمز مهم این است په این پتاب باعث مغشرود‬
‫پدهای پایتون بهتری بنویسمد‪.‬‬
‫ترفندهای پایتون این پتاب‪ ،‬عادتهای جدیدی در پدنویسرغ مرن بره وجرود آورد‪.‬‬
‫برای مثال اسرتفاده از ‪ exception‬هرای اختصاصرغ و ‪ AbstractBaseClasses‬باعرث‬
‫پدنویسغ بهتر من شد‪ ،‬چمزهایغ په به تنهایغ یادگرفتنشان سخت بود‪.‬‬

‫‪Bob Belderbos‬‬
‫مهندس ارشد شرپت ‪Oracle‬‬
‫مقدمه مترجمین‬

‫اگر درک پدهایتان پس از مدتغ دشوار است‪ ،‬بایرد بره ایرن ناتره توجره پنمرد پره‬
‫«سادگغ‪ ،‬نهایت پمچمدگغ است»‪ .‬ارلب اوقات پمچمده انگاشتن یک موضرو ‪ ،‬احسراس‬
‫قدرت به افراد مغدهد‪ .‬در هممن راستا مارتمن فاولر‪ ،‬مغگوید‪:‬‬
‫‪“Any fool can write code that a computer can understand. Good‬‬
‫”‪programmers write code that humans can understand.‬‬

‫این پتراب بوفرهای از ترفنردهای پرایتون اسرت پره باعرث مغشرود درک بهترری از‬
‫ویژگغهای زبان پایتون پسب پنمد‪ .‬در این پتراب روشهرایغ بررای پایتونمرک نوشرتن‬
‫پدها به همراه مثالهایغ پاربردی بمان شده است په در عرمن سرادگغ‪ ،‬باعرث افرزایی‬
‫توانایغ شما در پدنویسغ مغشود‪.‬‬
‫از آنزا په زبان انگلمسغ‪ ،‬زبان دنمای پامپموتر است‪ ،‬در ترجمهی ایرن پتراب سرعغ‬
‫بمران شرود‪ .‬در ایرن پتراب از‬ ‫شده است تا واژهها و نامهای مختلف یک عبارت خرا‬
‫تانم رکِ ترجمررهی ارتبرراطغ اسررتفاده شررده اسررت و هررمک پرردام از مفرراهمم برره صررورت‬
‫تحتاللفظغ ترجمه نشدهاند‪ .‬دلمل اینپرار انتقرال راحرت مفراهمم بره خواننرده و صردمه‬
‫ندیدن واژههای انگلمسغ و تانماال است‪.‬‬
‫اممدواریم از خواندن این پتاب لذت ببرید و در صورت ارائرهی هرگونره پمشرنهاد و‬
‫انتقاد در راستای بهتر شدن این پتاب‪ ،‬صمممانه پذیرای نظرات شما هستمم‪.‬‬

‫سمد محمد بابازاده‪ - 1‬ایممل‪[email protected] :‬‬


‫سمد معمن باباپور‪ - 2‬ایممل‪[email protected] :‬‬

‫‪1 https://boby.cloud‬‬
‫‪2 https://devmo.in‬‬
‫فهرست مطالب‬

‫فصل اول‪ :‬معرفی ‪15 .................................................................................................................................................‬‬


‫‪ .1-1‬ترفندهای پایتون چمست؟ ‪17......................................................................................................................................................‬‬
‫‪ .2-1‬این پتاب برای شما چه پاری انزام مغدهد؟‪19...................................................................................................‬‬
‫‪ .3-1‬این پتاب را چگونه مطالعه پنمم؟ ‪20................................................................................................................................‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪21 ......................................................................................................‬‬
‫‪ .1-2‬ویرگولهای آشفته ‪23......................................................................................................................................................................‬‬
‫‪ Assertion .2-2‬را پوششغ برای هرچمزی نانمد!‪26.......................................................................................................‬‬
‫‪ .3-2‬عبارت ‪ with‬و ‪ context manager‬در پایتون ‪36.................................................................................................‬‬
‫‪ .4-2‬آندرالین و داندرالین در پایتون ‪43.....................................................................................................................................‬‬
‫‪ .5-2‬حقمقتغ ناخوشایند در مورد رشتههای پایتون‪56......................................................................................................‬‬
‫‪ .6-2‬ذن پایتون ‪65...............................................................................................................................................................................................‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار‪69 ..........................................................................................................................‬‬
‫‪ .1-3‬توابع پایتون شهروندان درجه اول هستند ‪71.................................................................................................................‬‬
‫‪ .2-3‬المبداها‪ ،‬توابع تک جمله ای‪81..............................................................................................................................................‬‬
‫‪ .3-3‬قدرت اعزاب انگمز دپوریتورها ‪86...................................................................................................................................‬‬
‫‪ .4-3‬پاربردهای جالب ‪ *args‬و ‪100............................................................................................................... **kwargs‬‬
‫‪ .5-3‬بازپردن آرگومانهای توابع‪105...........................................................................................................................................‬‬
‫‪ .6-3‬همک چمز برای ‪108......................................................................................................................................................... Return‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪111 ......................................................................‬‬
‫‪ .1-4‬مقایسه شغء ها‪ "is" :‬در مقابل "=="‪113.....................................................................................................................‬‬
‫‪ .2-4‬تبدیل رشته (هر پالس به __‪ __repr‬نماز دارد) ‪116.....................................................................................‬‬
‫‪ .3-4‬پالسهای ‪ Exception‬سفارشغ‪125................................................................................................................................‬‬
‫‪ Clone .4-4‬اشماء برای تفریح و سود‪130.................................................................................................................................‬‬
‫‪ .5-4‬بررسغ ورا ت در پالسهای انتزاعغ پایه ‪138.........................................................................................................‬‬
‫‪ .6-4‬آشنایغ با ‪namedtuple‬ها ‪142........................................................................................................................................‬‬
‫‪ .7-4‬مشاالت شغء پالس در مقابل نمونهای از پالس ‪150..................................................................................‬‬
‫‪ .8-4‬نمونه‪ ،‬پالس و متدهای استاتمک به زبان ساده ‪156............................................................................................‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪169 .......................................................................................‬‬
‫‪Dictionary .1-5‬ها‪Map ،‬ها و ‪Hashtable‬ها‪172........................................................................................................‬‬
‫‪ .2-5‬انوا آرایههای پایتونغ ‪177.........................................................................................................................................................‬‬
‫‪struct .3-5‬ها و اشماء انتقال داده ‪187............................................................................................................................................‬‬
‫‪ .4-5‬مزموعهها در پایتون ‪197.............................................................................................................................................................‬‬
‫‪ .5-5‬پشتهها در پایتون (‪201.................................................................................................................................................. )LIFOs‬‬
‫‪ .6-5‬صفها در پایتون (‪205................................................................................................................................................)FIFOs‬‬
‫‪ .7-5‬صفهای با اولویت‪210.................................................................................................................................................................‬‬
‫فصل ششم‪ :‬حلقهها در پایتون‪215 .................................................................................................................‬‬
‫‪ .1-6‬نوشتن حلقههای پایتونغ ‪217......................................................................................................................................................‬‬
‫‪ .2-6‬ایزاد لمست در یک خط ‪220...................................................................................................................................................‬‬
‫‪ .3-6‬ترفندهایغ برای برش لمستها و استفاده از عملگر سوشغ ‪223................................................................‬‬
‫‪ .4-6‬آشنایغ بمشتر با اشماء قابل پممایی با حلقهها ‪227...................................................................................................‬‬
‫‪ .5-6‬مولدها در پایتون ‪236........................................................................................................................................................................‬‬
‫‪ .6-6‬آشنایغ بمشتر با مولدها ‪243..........................................................................................................................................................‬‬
‫‪ .7-6‬ساخت مولدها به صورت زنزمرهای ‪249........................................................................................................................‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪253 .....................................................................‬‬
‫‪ .1-7‬مقادیر پمی فرض در دیاشنریها ‪255............................................................................................................................‬‬
‫‪ .2-7‬مرتب سازی دیاشنریها ‪258...................................................................................................................................................‬‬
‫‪ .3-7‬پماده سازی عبارت ‪ switch/case‬با استفاده از دیاشنری ‪261..................................................................‬‬
‫‪ .4-7‬نااتغ په هنگام پار با دیاشنریها باید به آن ها توجه پنمد ‪265........................................................‬‬
‫‪ .5-7‬ادرام دیاشنریها با یادیگر ‪272.........................................................................................................................................‬‬
‫‪ .6-7‬چاپ و نمایی بهتر دیاشنریها ‪275.................................................................................................................................‬‬
‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪279 ..................................‬‬
‫‪ .1-8‬آشنایغ بمشتر با ماژولها در پایتون ‪281............................................................................................................................‬‬
‫‪ .2-8‬ایزوله سازی محمط پروژه با پمک ‪284........................................................................................ Virtualenv‬‬
‫‪ .3-8‬نگاهغ به پشت پردهی بایت پدها در پایتون ‪289..................................................................................................‬‬
‫فصل اول‪ :‬معرفغ‬

‫فصل اول‬

‫معـرفـی‬
‫‪ .1-1‬ترفندهای پایتون چیست؟‬
‫ترفندهای پایتونغ یک قطعه پوتاه پد از زبان برنامه نویسغ پایتون است په به عنوان‬
‫ابزاری برای آموزش مفاهمم عممق استفاده مغشود‪ .‬ترفند پایتون جنبه ای از پرایتون را برا‬
‫یک مثال ساده آموزش مغ دهد‪ .‬ترفند پایتون انگمزه ای در اختمار شرما قررار مغدهرد و‬
‫شما را قادر مغ سازد تا در زبان برنامه نویسغ پایتون عممق تر حفاری پنمد و یک درک‬
‫شهودی برای خود ایزاد پنمد‪.‬‬
‫ترفندهای پایتونغ به عنوان یک سری پوتاه تصاویر از پدها شرو شرد پره مرن در‬
‫یک هفته در تویمتر به اشتراک گذاشتم‪ .‬در پمال تعزب من‪ ،‬افراد بازخوردهای شگفت‬
‫انگمزی نشان دادنرد و بررای روزهرای متروالغ تاره پردهای مرن توسرط افرراد مختلرف‬
‫ریتویمت مغشد‪.‬‬
‫هرروز توسعه دهندگان بمشتری از من سؤال مغپردند په آیا روشغ بررای دسترسرغ‬
‫به پل سری آموزشغ وجود دارد یا نه‪ .‬درواقع‪ ،‬من فقرط چنرد مرورد از ایرن ترفنردها را‬
‫ارائه پرده بودم په موضوعات مختلف مربوط بره زبران برنامره نویسرغ پرایتون را شرامل‬
‫مغشد‪ .‬پشت سری آموزشغ من یک طرح جامع وجود نداشرت و فقرط یرک آزمرایی‬
‫جالب برای تفریح در تویمتر بود‪ .‬اما این سؤاالت از تاه پدهای من باعث شرد احسراس‬
‫پنم پدهای پوتاه و پاربردی من به عنوان ابزاری بررای تردریس عممرق زبران پرایتون‪،‬‬
‫ارزش سرمایهگذاری و پاوش را دارد‪ .‬سرانزام تصممم گرفتم چند ترفند پایتونغ دیگرر‬
‫تهمه پنم و آن ها را در قالب یک سری ایممل با افراد به اشتراک بگرذارم‪ .‬در طرغ چنرد‬
‫روز‪ ،‬صدها توسعه دهنده پایتون در اشتراک ایممل من بت نرام پردنرد و ایرن برازخورد‬
‫برای من بسمار شگفت انگمز بود‪.‬‬
‫طغ روزها و هفته های بعد‪ ،‬تعداد بسمار زیادی از توسعه دهندگان پایتون با من آشرنا‬
‫شدند‪ .‬آنها از من تشار مغپردند زیرا ترفندهای من باعث شده بود توسرعه دهنردگان‪،‬‬
‫‪  18‬ترفندهای پایتون‬

‫بخی هایغ از زبان برنامه نویسغ پایتون په در حال دست و پنزه نررم پرردن برا مفراهمم‬
‫آن بودند را به خوبغ درک پنند‪ .‬من فارمغپردم این ترفندها فقط یک سری تصراویر‬
‫ساده از تاه پدهای پایتون هستند‪ .‬اما بسماری از توسرعه دهنردگان از طریرق ایرن پردها‬
‫مفاهمم عممقغ را آموختند و مشاهده این بازخوردها برای من بسمار خوشحالپننده بود‪.‬‬
‫پس از این من تصرممم گررفتم تزربمراتم را در دیگرر زممنره هرای ترفنردهای پرایتون‬
‫افزایی دهم و آن را در یک سری ‪ 30‬عددی ایممل به اشتراک بگذارم‪ .‬هرپدام از این‬
‫ترفندها دارای یک عنوان و یک تصویر از پد بود و من به زودی متوجه محدودیتهای‬
‫اشتراک گذاری در این قالب شدم‪ .‬در آن زمان یک توسعه دهنده نابمنرا پرایتون بره مرن‬
‫ایممل داد و از ایناه نمغتوانست این تصاویر را برای خود بخواند نااممد شده بود‪.‬‬
‫واضح است برای ایناه من بتوانم محتوا را در دسترس مخاطبان گسرترده ترری قررار‬
‫دهم باید زمان بمشتری را روی آن صرف مغپردم‪ .‬بنابراین‪ ،‬من نشستم ترا دوبراره تمرام‬
‫ایمملهای ترفندهای پایتون را با متن ساده و با برجسته سازی مبتنغ بر ‪ HTML‬بازنویسرغ‬
‫پنم‪ .‬این بازنویسغ باعث شد پاس هایغ را دریافرت پرنم و توسرعه دهنردگان از ایناره‬
‫مغتوانستند تاه پدها را ‪ Copy/Paste‬پنند و با آن پار پنند‪ ،‬خوشحال بودند‪.‬‬
‫هرچه توسعه دهندگان بمشرتری در ایرن سرری ایممرل برت نرام مغپردنرد‪ ،‬از طریرق‬
‫دریافت بازخوردها متوجه الگوی خاصغ شدم‪ .‬برخغ از ترفندها بسمار انگمزشغ بودند و‬
‫افراد مفاهمم عممق را به خوبغ متوجه مغشدند‪ ،‬اما برای ترفندهای پمچمده ترر راهنمرایغ‬
‫وجود ن داشت په منابع بمشتری را در اختمار توسعه دهندگان مشتاق قرار دهد و به درک‬
‫عممقتر آنها از زبان برنامه نویسغ پایتون جامه عمل بپوشاند‪.‬‬
‫بمایمررد فقررط بگررویمم پرره ایررن یاررغ دیگررر از رسررالتهای مررن در وبسررایت‬
‫‪ realpython.com‬بود په به پمک آن بتروانم توسرعه دهنردگان بمشرتری را برا مفراهمم‬
‫پمچمده و عممق پایتون آشنا پرنم‪ .‬بنرابراین مرن تصرممم گررفتم بهتررین و ارزشرمندترین‬
‫ترفندهای پایتون را از سری ایمملها جمع آوری پنم و با توضمحات بمشتر در قالب یک‬
‫پتاب ارزشمند منتشر پنم‪.‬‬
‫فصل اول‪ :‬معرفی ‪19 ‬‬

‫‪ ‬پتابغ په با نمونه پدهای پوتاه و قابل هضم په جالرب تررین جنبره هرای زبران‬
‫برنامه نویسغ پایتون را آموزش مغدهد‪.‬‬
‫‪ ‬پتابغ په مانند بوفه ای از ویژگغ های بسمار جذاب زبان برنامره نویسرغ پرایتون‬
‫عمل مغپند و مغتواند سطح انگمزه شما را در هنگام توسعه پرایتون براال ببررد و‬
‫هربخی به صورت جداگانه قابل مطالعه باشد‪.‬‬
‫‪ ‬پتابغ په دست شما را مغگمرد و شما را به اعمراق زبران برنامره نویسرغ پرایتون‬
‫مغبرد‪.‬‬
‫این پتاب شامل آزمایشات و تزربمات بسمار من در زبان برنامه نویسغ پایتون است و‬
‫از عشق من به این زبان برنامه نویسغ بره وجرود آمرده اسرت‪ .‬اممردوارم پره شرما هرم از‬
‫خواندن این پتاب لذت ببرید و ترفندهای جدیدی را در رابطره برا زبران برنامره نویسرغ‬
‫پایتون یاد بگمرید‪.‬‬

‫‪ .2-1‬این کتاب برای شما چه کاری انجام میدهد؟‬


‫این پتاب تانمک هایغ را به شما مغآموزد تا تبدیل به یک توسعه دهنده زیررک و‬
‫با بصمرت در پایتون شوید‪ .‬شاید تعزرب پنمرد پره چگونره خوانردن ایرن پتراب باعرث‬
‫مغشود تا به این موارد دست یابمم؟‬
‫ترفندهای پایتون یک آموزش گام به گام پایتون نمست‪ .‬یک آموزش مقدماتغ برای‬
‫ورود به زبان برنامه نویسغ پایتون نمست‪ .‬اگر در مراحرل ابتردایغ یرادگمری زبران برنامره‬
‫نویسغ پایتون هستمد‪ ،‬این پتاب به تنهایغ شما را تبدیل به یک توسرعه دهنرده حرفره ای‬
‫پایتون نمغپند‪ .‬خواندن این پتاب برای شما سودمند خواهد بود اما باید اطممنان حاصل‬
‫پنمد په با مطالعه منابع دیگرر مهرارت هرای بنمرادین زبران برنامره نویسرغ پرایتون را یراد‬
‫بگمرید‪.‬‬
‫اگر شما قبال دانی زبان برنامه نویسغ پایتون را یاد گرفته اید و مغخواهمد بره سرطح‬
‫بعدی بروید به شما تبریک مغگویم‪ .‬زیرا مغتوانمد بمشترین بهره را از این پتاب ببریرد‪.‬‬
‫‪  20‬ترفندهای پایتون‬

‫این پتاب بررای پسرانغ پره قربال بررای مردتغ برا زبران پرایتون پرار پردهانرد و اپنرون‬
‫مغخواهند عممق تر با این زبان برنامه نویسغ آشنا شوند تا پدهایشان را سریع تر و بهمنره‬
‫تر بنویسند‪ ،‬بسمار مناسب است و مغتواند باعث شود پدهای پایتونمک بهتری بنویسند‪.‬‬

‫‪ .3-1‬این کتاب را چگونه مطالعه کنیم؟‬


‫بهترین راه برای خواندن پتاب ترفندهای حرفه ای پایتون آن است په با ایرن پتراب‬
‫همانند یک بوفه ای شامل از ویژگغ های فوق العاده پایتون رفتار پنمد‪ .‬هر ترفند موجود‬
‫در این پتاب به صورت جداگانه توضمح داده شده اسرت و شرما مغتوانمرد مسرتقمما بره‬
‫مبحثغ په نماز دارید بروید و آن را مطالعه پنمد‪ .‬من هم شما را به انزام این پار توصرمه‬
‫مغپنم‪.‬‬
‫البته شما مغتوانمد تمام ترفندهای پایتون را بره ترتمبرغ پره در پتراب ذپرر شرده نمرز‬
‫بماموزید‪ .‬به این ترتمب همک یک از ترفندها را از دست نخواهمرد داد و وقترغ بره صرفحه‬
‫آخر پتاب برسمد با تمام ترفندهای حرفه ای زبان برنامه نویسغ پایتون آشنا شده اید‪.‬‬
‫فهممدن بسماری از این ترفنردها آسران اسرت و هرمک مشرالغ نخواهمرد داشرت‪ .‬شرما‬
‫بالفاصله بعد از خواندن و یرادگمری یرک ترفنرد مغتوانمرد از آن در پرار روزانره خرود‬
‫استفاده پنمد‪ .‬اما برای بعضغ از ترفندهای پمچمده ممان است نماز به پمرغ زمران بررای‬
‫پاوش و تمرین داشته باشمد‪.‬‬
‫فصل دوم‪ :‬پدنویسغ تممز با پایتون‬

‫فصل دوم‬

‫کدنویسی تمیز با پایتون‬


‫‪ .1-2‬ویرگولهای آشفته‬
‫یک ناته مفمد برای اضافه یا حذف پردن آیتمها از لمسرت‪ ،‬دیاشرنری یرا سرت در‬
‫پایتون این است په حتما تمام خطوط خود را با پاما (ویرگول) تمام پنمد‪.‬‬
‫مطمئن نمستمد په راجع به چه چمرزی صرحبت مرغپنمم؟ بگذاریرد یرک مثرال سراده‬
‫بزنمم‪ .‬فرض پنمد په یک لمست از نام های مختلف در پد دارید‪.‬‬

‫]'‪>>> names = ['Alice', 'Bob', 'Dilbert‬‬

‫زمانغ په روی نامهای این لمست تغممر ایزاد پنمد‪ ،‬خملرغ سرخت اسرت برا مشراهده‬
‫خروجغ دستور ‪ git diff‬بفهممد نامها چه تغممراتغ پردند‪ .‬زیرا اپثر سمستم هرای پنتررل‬
‫پد منبع بر اساس شماره خط پد هستند و بره سرختغ مغتواننرد تغممررات در یرک خرط‬
‫واحد را برجسته پنند‪.‬‬
‫یک راه سریع حل این مشال‪ ،‬اتخراذ یرک سربک پرد نویسرغ جدیرد اسرت پره در آن‬
‫آیتمهای لمست‪ ،‬دیاشنری یا ست در پایتون را در چند خرط متروالغ بنویسرمد‪ .‬هماننرد مثرال‬
‫زیر‪.‬‬

‫[ = ‪>>> names‬‬
‫‪'Alice',‬‬
‫‪'Bob',‬‬
‫'‪'Dilbert‬‬
‫]‬

‫اگر لمست خود را به صورت باال تعریف پنمد‪ ،‬در هر خط یک مرورد وجرود دارد و‬
‫زمانغ په در سمستم پنترل پد منبع خود تغممرات را مشاهده پنمد‪ ،‬بره راحترغ مغتوانمرد‬
‫متوجه شوید پدام نام ها اضافه‪ ،‬حذف یا اصالح شده اند‪ .‬ایرن یرک تغممرر پوچرک در‬
‫سبک پد نویسغ است امرا تزربره مرن نشران مغدهرد از اشرتباهات احمقانره جلروگمری‬
‫مغپند‪ .‬اشتباهاتغ په ممان است برطرف شدن آن ساعت ها زمان ببررد‪ .‬همچنرمن ایرن‬
‫تغممر باعث شد همتممغ های من بتوانند راحت تر پدهای من را بررسغ پنند‪.‬‬
‫‪  24‬ترفندهای پایتون‬

‫اپنون دو مورد دیگر وجود دارد په ممان است باعث سردرگمغ شرود‪ .‬هرر وقرت‬
‫یک مورد جدید را در انتهای لمست اضافه پنمد یا آخرین مورد را حذف پنمرد بایرد بره‬
‫صورت مداوم جای ویرگول را به روز رسانغ پنمد‪.‬‬

‫‪ .1-1-2‬بهم ریختن کد با ویرگولهای نامناسب‬


‫فرض پنمد مغخواهمم نام دیگرری (‪ )Jane‬را بره لمسرت اضرافه پنرمم‪ ،‬اگرر ‪ Jane‬را‬
‫اضافه پنمد‪ ،‬مزبور هستمد بعد از ‪ Dilbert‬یک پاما قرار دهمد و اگر فرامروش پنمرد‪ ،‬برا‬
‫نتمزهای رمر قابل انتظار روبرو مغشوید‪.‬‬

‫[ = ‪>>> names‬‬
‫‪'Alice',‬‬
‫‪'Bob',‬‬
‫!‪'Dilbert' # <- Missing comma‬‬
‫'‪'Jane‬‬
‫]‬

‫اگر قرار دادن عالمت ویرگول را فراموش پنمرد‪ ،‬ممارن اسرت هنگرام اجررای پرد‬
‫متعزب شوید‪.‬‬

‫‪>>> names‬‬
‫]'‪['Alice', 'Bob', 'DilbertJane‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬پایتون در زمان اجرا‪ ،‬رشته های ‪ Dilbert‬و ‪ Jane‬را به‬
‫‪ DilbertJane‬تبدیل پرده و در واقع آنها را بهم چسباند‪ .‬به این عمرل در پرایتون جمرع‬
‫بندی دقمق رشتهها‪ 1‬مغگویند په یک رفتار طبمعغ در زبران پرایتون اسرت‪ .‬امرا مغتوانرد‬
‫گاهغ اوقات با ایزاد مشاالتغ در برنامه پایتونغ‪ ،‬شما را تا مرز پرتاب پردن مرانمتور از‬
‫پنزره به بمرون پمی ببرد‪.‬‬
‫با این وجود‪ ،‬جمع بندی دقمرق رشرته هرا در پرایتون بسرمار مفمرد اسرت‪ .‬بررای مثرال‪،‬‬
‫مغتوانمد از این روش برای زمانغ استفاده پنمد‪ ،‬په مغخواهمد یک رشته مرتن طروالنغ‬

‫‪1 String Literal Concatenation‬‬


‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪25 ‬‬

‫را در برنامه خود وارد پنمد اما نمغ خواهمد پره از عالمرت برک اسرلی (یرا \) اسرتفاده‬
‫پنمد‪.‬‬

‫' ‪my_str = ('This is a super long string constant‬‬


‫' ‪'spread out across multiple lines.‬‬
‫)'!‪'And look, no backslash characters needed‬‬

‫از طرف دیگر هممن االن دیدیم په این قابلمت مفمد‪ ،‬چطور مغتواند به یرک بحرران‬
‫در یک برنامه تزاری تبدیل شده و باعث ایزاد باگهایغ عزمب شود و به یرک بردهغ‬
‫فنغ تبدیل شود‪ .‬حال چگونه این مشال را حل پنمم؟‬
‫اضافه پردن پامای گم شرده‪ ،‬پرس از ‪ Dilbert‬باعرث مغشرود جلروی ایرن مشرال‬
‫گرفته شده و دو رشته ‪ Dilbert‬و ‪ Jane‬از یادیگر جدا شوند‪.‬‬

‫[ = ‪>>> names‬‬
‫‪'Alice',‬‬
‫‪'Bob',‬‬
‫‪'Dilbert',‬‬
‫'‪'Jane‬‬
‫]‬

‫اما اپنون دور یک حلقه چرخمدیم و دوباره به نقطه ابتدایغ بازگشرتمم‪ .‬بررای اضرافه‬
‫پردن یک نام جدید به لمست مزبور شدیم دو خط از پد را تغممر دهرمم‪ .‬ایرن موضرو‬
‫باعث مغشود دیدن تغممرات پد منبع در خروجغ دستور ‪ git diff‬سخت تر شود‪ .‬آیا نام‬
‫جدیدی اضافه شده است یا نام ‪ Dilbert‬تغممر پرده است؟‬
‫خوشبختانه راه های پایتونمک باعث مغشوند تا مشال قرار دادن پاما را یرک برار و‬
‫برای هممشه حل پنمم‪ .‬برای حل دائمغ این مشال‪ ،‬باید خود را آموزش دهمرد ترا یرک‬
‫سبک پدنویسغ مشخص را اتخاذ پنمد تا در وهله ی اول جلوی این ایزاد ایرن مشرال‬
‫گرفته شود‪ .‬صبر پنمد تا به شما نشان دهمم چطور مغتوانمد این پار را انزام دهمد‪.‬‬
‫در پایتون‪ ،‬مغتوانمد ویرگول را بعد از هر مورد در لمسرت‪ ،‬سرت یرا دیاشرنری قررار‬
‫دهمد از جمله آخرین مورد موجود در پالاشرنهرای نرام بررده شرده‪ .‬بنرابراین بره خرود‬
‫‪  26‬ترفندهای پایتون‬

‫آموزش دهمد په همواره به یاد داشته باشمد انتهای خطوط خود را با ویرگرول بره اتمرام‬
‫برسانمد و از ایناه در خط انتهایغ ویرگول قرار ندهمد‪ ،‬پرهمز پنمد‪.‬‬
‫برای مثال به قطعه پد زیر نگاه پنمد‪.‬‬

‫[ = ‪>>> names‬‬
‫‪'Alice',‬‬
‫‪'Bob',‬‬
‫‪'Dilbert',‬‬
‫]‬

‫بعد از اسم ‪ Dilbert‬ویرگول را مشاهده مغپنمد؟ این پار باعث مغشود بدون نماز به‬
‫جایگذاری مزدد ویرگول‪ ،‬بتوانمد لمست خود را آپدیت پنمرد و خطروط پرد را ابرت‬
‫نگه دارید‪ .‬این روش‪ ،‬پنترل پد منبرع شرما را سرادهتر پررده و افرراد‪ ،‬پردهای شرما را‬
‫خوشحالتر بازبمنغ مغپنند‪ .‬گاهغ اوقرات جرادو در چمزهرای پوچرک اسرت‪ ،‬درسرت‬
‫است؟‬

‫‪ .2-1-2‬نکات کلیدی در هنگام کار با ویرگولها در پایتون‬


‫‪ ‬اصالح قالب بندی پد و قرار دادن پاما باعث مغشود لمست‪ ،‬ست یا دیاشرنری‬
‫در برنامهی شما قابلمت مدیریت راحت تری داشته باشد‪.‬‬
‫‪ ‬جمع بندی دقمق رشتههای پایتون ممارن اسرت فوایرد زیرادی بررای شرما داشرته‬
‫باشد‪ .‬اما همچنمن ممان است باعث بره وجرود آمردن براگ هرایغ شرود پره بره‬
‫سختغ قابل حل هستند‪.‬‬

‫‪ Assertion .2-2‬را پوششی برای هرچیزی نکنید!‬


‫گاهغ اوقات یک ویژگغ مفمد و پاربردی در یک زبان برنامه نویسرغ مرورد توجره‬
‫پمتری نسبت به سایر ویژگغها قرار مغگمرد‪ .‬این اتفاقغ است په برای عبرارت ‪assert‬‬
‫در پایتون افتاده است‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪27 ‬‬

‫در این بخی مغخواهمم مقدمه ای برای استفاده از عبارت ‪ assert‬در پایتون را بمران‬
‫پنمم‪ .‬شما مغآموزید په چطور از عبارت ‪ assert‬برای آشاارسازی خودپار خطاها در‬
‫پد پایتون استفاده پنمد‪ .‬این ویژگغ باعث مغشود برنامه ی شما قابلمت اطممنان بمشرتری‬
‫داشته باشد و به راحتغ قابل دیباگ باشد‪.‬‬
‫در این مرحله ممان است برای شما سؤال ایزاد شود پره عبرارت ‪ assertion‬دقمقرا‬
‫چه چمزی هست و برای استفاده در چه جاهایغ مناسب است؟ بمایمد به این سؤال اساسغ‬
‫پاس دهمم‪.‬‬
‫در هسته زبان برنامه نویسغ پایتون‪ ،‬عبارت ‪ assert‬یک ابزار مفمد برای دیباگ پدها‬
‫بوده په یک شرایط خاصغ را آزمایی مغپند‪ .‬اگر مقردار عبرارت ‪ assert‬برابرر ‪true‬‬
‫شود‪ ،‬اتفاقغ رخ نمغدهد و برنامه به خوبغ بره اجررای خرود ادامره مغدهرد‪ .‬اگرر مقردار‬
‫عبارت ‪ assert‬برابرر ‪ false‬شرود‪ ،‬خطرای ‪ AssertionError‬رخ داده و شرما بره راحترغ‬
‫خطاهای برنامه ی خود را پمدا خواهمد پرد‪.‬‬
‫پلمه ‪ assert‬در لغت به معنغ ادعا پردن و دفا پردن اسرت‪ .‬بنرابراین در پردهای‬
‫خود ادعا مغپنمد په شرایط خاصغ باید برقرار باشد‪ ،‬در رمر این صورت با ایزاد یرک‬
‫خطا توسعه دهنده را مطلع خواهمد پرد‪.‬‬

‫‪ Assert .1-2-2‬در پایتون با یک مثال ساده‬


‫در اینزا یک مثال ساده وجود دارد تا ببمنمد ‪ Assertion‬در پزا مغتواند مفمد باشرد‪.‬‬
‫من سعغ پردم جنبه ای از یک مشال واقعغ را په شرما واقعراد در یارغ از برنامره هرای‬
‫خود با آن روبرو مغشوید‪ ،‬بمان پنم‪.‬‬
‫فرض پنمد شما در حال ساختن یک فروشگاه آنالین با پایتون هستمد‪ .‬شرما در حرال‬
‫توسعه هستمد تا یک تابع تخفمف پوپن را به سمسرتم اضرافه پنمرد و چنرمن ترابعغ را بره‬
‫هممن منظور مغنویسمد‪.‬‬
‫‪  28‬ترفندهای پایتون‬

‫‪def apply_discount(product, discount):‬‬


‫))‪price = int(product['price'] * (1.0 - discount‬‬
‫]'‪assert 0 <= price <= product['price‬‬
‫‪return price‬‬

‫به عبارت ‪ assert‬در پد باال توجه پردید؟ نرخ تخفمف محاسبه شده توسط این تابع‬
‫نمغ تواند پمتر از صفر دالر و بمشتر از قممت اصلغ محصول باشرد‪ .‬اگرر از ایرن قابلمرت‬
‫برای اعمال تخفمف معتبر استفاده پردیم اپنون باید اطممنران حاصرل پنرمم پره فرآینرد‬
‫مورد نظر به درستغ انزام مغشود‪.‬‬
‫برای مثال محصوالت فروشگاه به صورت دیاشرنری هرایغ سراده ذخمرره مغشروند‪.‬‬
‫شاید این روشغ نباشد په در یک برنامه واقعرغ بره پرار رود‪ ،‬امرا مغتروان بررای ا برات‬
‫عملارد ‪ Assertion‬از این روش استفاده پرد‪ .‬بنابراین یک محصرول جدیرد مغسرازیم‬
‫په شامل ‪ 2‬مقدار نام محصرول (یرک جفرت پفری) و قممرت محصرول (‪ 14900‬دالر)‬
‫است‪.‬‬

‫}‪>>> shoes = {'name': 'Fancy Shoes', 'price': 14900‬‬

‫اپنون بمایمد یک تخفمف ‪ 25‬درصدی به محصول اعمرال پنرمم و انتظرار داریرم پره‬
‫قممت محصول معادل ‪ 111.75‬دالر شود‪.‬‬

‫)‪>>> apply_discount(shoes, 0.25‬‬


‫‪11175‬‬

‫بسمارعالغ‪ ،‬اپنون بمایمد یک مقدار تخفمف نامعتبر وارد پنمم‪ .‬برای مثرال‪ ،‬مرن ‪%200‬‬
‫تخفمف به محصول اعمال مغپنم‪.‬‬

‫)‪>>> apply_discount(shoes, 2.0‬‬


‫‪Traceback (most recent call last):‬‬
‫>‪File "<input>", line 1, in <module‬‬
‫)‪apply_discount(prod, 2.0‬‬
‫‪File "<input>", line 4, in apply_discount‬‬
‫]'‪assert 0 <= price <= product['price‬‬
‫‪AssertionError‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪29 ‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬زمانغ په سعغ پردیم از یک تخفمف نامعتبر اسرتفاده‬


‫پنمم‪ ،‬نرم افزار با ‪ Assertion Error‬متوقف مغشود‪ .‬دلمل این امر آن است پره در ترابع‬
‫مورد نظر‪ ،‬حاصل عبارت ‪ assert‬نقض شده و مقدار آن برابر با ‪ False‬شده است‪.‬‬
‫در اطالعات خروجغ خطا‪ ،‬مغتوانمد به صورت دقمق ببمنمد چه مشالغ در برنامه شما‬
‫رخ داده است‪ .‬برای مثال اگر شما یا یک نفر از توسعه دهندگان تمم با این خطا رو به رو‬
‫شود به سرعت مغتواند دلمل به وجود آمدن مشال را متوجه شود‪.‬‬
‫با این پار سرعت شما در رفع مشاالت برنامه بمشتر خواهرد شرد و مغتوانمرد برنامره‬
‫های تزاری خود را در بلند مدت به راحترغ مردیریت پنمرد‪ .‬دوسرت مرن‪ ،‬ایرن قردرت‬
‫‪ assertion‬است!‬

‫‪ .2-2-2‬چرا از عبارات منظم‪ 1‬استفاده نکنیم؟‬


‫اپنون ممان است از خود سؤال پنمد په چرا در مثال قبل تنها یرک شررط ‪ if‬بررای‬
‫پنترل فرآیند نگذاشتمم؟ آیا ساده تر نبود؟‬
‫همانطور په احتماال متوجه شدهاید‪ ،‬هدف استفاده از ‪ assertion‬آگاه سازی توسرعه‬
‫دهندگان برای پشف خطاهایغ هست په در برنامه پشف نشده اند‪ Assertion .‬روشغ‬
‫برای نشان دادن حالت های مختلف خطای برنامه همانند ‪ file not found error‬نمست‪.‬‬
‫اگر برنامه شما فاقد همچگونه اشاالغ باشرد پرس ایرن شررایط هرگرز در برنامره شرما‬
‫اتفاق نمغافتد! امااگر اشاالغ در برنامه شما به وجرود آمرد‪ ،‬برنامره برا یرک ‪assertion‬‬
‫‪ error‬متوقف خواهد شد و برنامه به شما مغگوید دقمقا در پدام قسمت یک اتفاق رمرر‬
‫منتظره افتاده است‪ .‬این پار رفع خطای برنامه را ساده ترر مغپنرد‪ .‬مرن عاشرق چمزهرایغ‬
‫هستم په زندگغ را راحت تر مغپنند‪ .‬شما چطور؟‬
‫در حال حاضر به یاد داشته باشمد په عبارت ‪ assert‬در پایتون روشرغ بررای ‪debug‬‬
‫است نه برای مقابله با خطاهای زمان اجرا‪ .‬هدف اصلغ ‪ assertion‬این است په توسرعه‬

‫‪1 Regular Expressions‬‬


‫‪  30‬ترفندهای پایتون‬

‫دهندگان به سرعت متوجه ریشه پدید آمدن خطا شوند و خطرا را سرریع ترر رفرع پننرد‪.‬‬
‫یک ‪ assertion error‬در پد شما رخ نمغدهد مگر در حالتغ په برنامهی نوشرته شرده‬
‫دچار خطا شود‪.‬‬
‫بمایمد یک نگاه نزدیرک ترر بره پارهرایغ پره مغتروانمم برا ‪ assertion‬انزرام دهرمم‬
‫بمندازیم و سپس دو اشرتباه مترداول هنگرام اسرتفاده از ایرن عبرارت در دنمرای واقعرغ را‬
‫بررسغ پنمم‪.‬‬

‫‪ .3-2-2‬سینتکس ‪ assert‬در پایتون‬


‫هممشه بهتر است قبل از استفاده از یک قابلمت در یک زبان برنامه نویسغ‪ ،‬ببمنمم ایرن‬
‫قابلمت چطور پمراده سرازی شرده اسرت‪ .‬برا توجره بره مسرتندات رسرمغ پرایتون‪ ،‬نگراهغ‬
‫مغاندازیم به نحوه پماده سازی عبارت ‪ assert‬در این زبان برنامه نویسغ‪.‬‬

‫]‪assert_stmt ::= "assert" expression1 ["," expression2‬‬

‫در عبارت فوق‪ expression1 ،‬شررایطغ هسرت پره آن را تسرت مرغپنمم و مقردار‬
‫اختماری ‪ expression2‬پمامغ است په مغخواهمم زمانغ په خطا رخ داد نمایی دهرمم‪.‬‬
‫در زمان اجرای برنامه پایتونغ‪ ،‬مفسر پایتون هرپدام از ‪ expression‬ها را به صورت زیر‬
‫پردازش مغپند‪.‬‬

‫‪if __debug__:‬‬
‫‪if not expression1:‬‬
‫)‪raise AssertionError(expression2‬‬

‫دو ناته جالب در رابطه با پد فوق‬


‫‪ .1‬قبل از ایناره عبرارت ‪ assert‬چرک شرود‪ ،‬متغمرر سراسرری __‪ __debug‬چرک‬
‫مغشود‪ .‬این متغمر‪ ،‬زمانغ په برنامه در حالت توسعه است برابرر برا ‪ true‬و هنگرامغ پره‬
‫برنامه برای محمط پروداپشن بهمنه سازی مغشود برابر با ‪ false‬است‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪31 ‬‬

‫همچنررمن شررما مغتوانمررد از مقرردار ‪ expression2‬اسررتفاده پنمررد تررا یررک پمررام خطررا‬
‫اختصاصغ نمایی دهمد‪ .‬این پار رفع مشال را راحت تر مغپند‪ .‬برای مثال به قطعه پد‬
‫زیر دقت پنمد‪.‬‬

‫‪>>> if cond == 'x':‬‬


‫)(‪do_x‬‬
‫‪elif cond == 'y':‬‬
‫)(‪do_y‬‬
‫‪else:‬‬
‫( ‪assert False,‬‬
‫' ‪'This should never happen, but it does‬‬
‫‪'occasionally. We are currently trying to‬‬
‫' ‪…………………………….'figure out why. Email dbader if you‬‬
‫)'!‪'encounter this in the wild. Thanks‬‬

‫این پمام زشت است؟ موافقم‪ ،‬اما بسمار پاربردی است زمانغ په شما با یرک خطرای‬
‫هایزنباگ‪ 1‬در برنامه خود مواجه مغشوید‪.‬‬

‫‪ .4-2-2‬اشتباهات متداول هنگام استفاده از عبارت ‪ assert‬در پایتون‬


‫قبل از ادامه‪ ،‬دو خطای متداول در مورد استفاده از عبارت ‪ assert‬در پرایتون وجرود‬
‫دارد په مغخواهم راجع به آنها توضمح دهم‪.‬‬
‫مورد اول باعث مخاطرات امنمتغ در برنامه شما مغشود و مرورد دوم یرک سرمنتاس‬
‫عزمب و رریب است په نوشتن ‪ asertion‬ها را بغ فایده مغپند‪.‬‬
‫رخ دادن این اشتباهات پامالد وحشتناک است بنابراین به هشدارهای زیر توجه پنمد‪.‬‬

‫‪ .5-2-2‬هشدار اول‪ :‬از ‪ assert‬برای اعتبارسنجی داده ها استفاده نکنید‬


‫بزرگترین اشتباه رایج استفاده از عبارت ‪ assert‬بررای اعتبرار سرنزغ داده هرا اسرت‪.‬‬
‫همانطور په مغدانمد در محمطهای پروداپشن اجرای برنامه با اسرتفاده از سرویمک ‪ -O‬و‬
‫‪ -OO‬عبارت ‪ assertion‬بره صرورت سراسرری رمرفعرال مغشرود‪ .‬همرانطور پره متغمرر‬

‫‪1 Heisenbug‬‬
‫‪  32‬ترفندهای پایتون‬

‫محمطغ ‪ PYTHONOPTIMIZE‬برای بهمنه سازی برنامه به صورت سراسری در ‪CPython‬‬


‫فعال مغشود‪.‬‬
‫این پار باعث مغشود تمرام عبرارت هرای ‪ assertion‬برابرر برا ‪ null-operation‬یرا‬
‫فرآیندهای خالغ شوند‪ .‬عبارت های ‪ assert‬به سادگغ جمع آوری شرده و دیگرر اجررا‬
‫نمغشوند‪ .‬این موضو به این معنغ است په همک پدام از عبارات شررطغ درون ‪assert‬‬
‫دیگر اجرا نخواهند شد‪.‬‬
‫این تصممم در زمان طراحغ داخلغ زبران برنامره نویسرغ پرایتون گرفتره شرده اسرت‪،‬‬
‫همانطور په در زبان های برنامه نویسغ دیگر نمز وجود دارد‪ .‬بنابراین‪ ،‬استفاده از عبارات‬
‫شرطغ در ‪ assert‬برای اعتبار سنزغ داده ها پاری بسمار خطرناک است‪.‬‬
‫بگذارید پمغ بمشتر توضمح دهم‪ .‬اگرر برنامره شرما از یرک عبرارت ‪ assert‬اسرتفاده‬
‫مغپند تا مقدار درست یا رلط بودن خروجرغ یرک ترابع را بررسرغ پنرد‪ ،‬مغتوانرد بره‬
‫سرعت تبدیل به یک آتی سوزی در پدهای شما شود و خطاها یرا نقرص هرای امنمترغ‬
‫مهلاغ را به وجود بماورد‪.‬‬
‫یک مثال ساده را برای بررسغ این مشال مطرح مغپنمم‪ .‬یک بار دیگر تصور پنمد‬
‫په شما با استفاده از پایتون یک برنامه فروشگاه آنالین را ایزراد پردهایرد‪ .‬در جرایغ از‬
‫برنامه تاه پدی وجود دارد په پاربر مغتواند محصولغ را در فروشگاه حذف پنرد‪ .‬از‬
‫آنزا په شما به تازگغ عبارات ‪ assert‬را آموختره ایرد‪ ،‬مشرتاق هسرتمد پره از آنهرا در‬
‫پدهای خود استفاده پنمد و پماده سازی په در زیر مغبمنمد را برای حذف محصروالت‬
‫انزام مغدهمد‪.‬‬

‫‪def delete_product(prod_id, user):‬‬


‫'‪assert user.is_admin(), 'Must be admin‬‬
‫‪assert store.has_product(prod_id), 'Unknown‬‬
‫'‪product‬‬
‫)(‪store.get_product(prod_id).delete‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪33 ‬‬

‫به تابع ‪ delete_product‬نگاه دقمق تری بمندازید‪ .‬اپنون اگر ‪ assertion‬ها در زمان‬
‫اجرا رمرفعال شوند چه اتفاقغ رخ مغدهد؟ در ‪ 3‬خط پد فروق‪ 2 ،‬مشرال بسرمار جردی‬
‫وجود دارد و به دلمل استفاده اشتباه از عبارات ‪ assert‬پدید آمده است‪.‬‬
‫‪ .1‬چک پردن مزوز مدیرپل با اسرتفاده از ‪ assert‬یرک خطرر مهلرک اسرت‪ .‬اگرر‬
‫‪ assertion‬ها رمرفعرال شروند ایرن شررط بره یرک شررط ‪ null‬تبردیل مغشرود‪ .‬اپنرون‬
‫هرپاربری مغتواند محصوالت را حذف پند! بررسغ مزوز هرگز اجرا نمغشود و یک‬
‫در پشتغ (‪ )backdoor‬برای نفوذگران باز شرده پره بتواننرد تمرام محصروالت برنامره را‬
‫پاک پنند و به یاپارچگغ داده ها آسمب برسانند‪.‬‬
‫‪ .2‬تابع ‪ has_product‬بره دلمرل رمرفعرال شردن ‪ assertion‬هرا اجررا نمغشرود‪ .‬ایرن‬
‫موضررو برره ایررن معنررغ اسررت پرره تررابع ‪ get_product‬اپنررون برردون مقرردارهای معتبررر‬
‫‪ prod_id‬مغتوانرد فراخروانغ شرود‪ .‬بنرابراین برا اینارره محصرول وجرود نردارد امرا تررابع‬
‫‪ get_product‬به دلمل اشتباه در استفاده از ‪ assertion‬فراخوانغ مغشود! ایرن مخراطره‬
‫امنمتغ مغتواند باعث حمله ‪ DDoS‬در برنامه شما شود‪ .‬زیرا یرک نفروذگر مغتوانرد داده‬
‫های اشتباه بسمار زیادی را برای حذف پردن محصول به ترابع ‪ get_product‬بفرسرتد و‬
‫باعث از دسترس خارج شدن برنامه گردد‪.‬‬
‫اپنون اگر از خود مغپرسمد چطور جلوی این مشاالت را بگمریم‪ ،‬پاس بسمار ساده‬
‫است‪ .‬از ‪ assertion‬بررای اعتبارسرنزغ داده هرا اسرتفاده نانمرد‪ .‬درعروض مغتوانمرد برا‬
‫استفاده از عبارت شرطغ ‪ if‬شروط موردنظر را چک پنمرد و در صرورتغ پره شررط هرا‬
‫نقض شدند‪ ،‬یک خطای اختصاصغ تولمد پنمد‪.‬‬

‫‪def delete_product(product_id, user):‬‬


‫‪if not user.is_admin():‬‬
‫)'‪raise AuthError('Must be admin to delete‬‬
‫‪if not store.has_product(product_id):‬‬
‫)'‪raise ValueError('Unknown product id‬‬
‫)(‪store.get_product(product_id).delete‬‬
‫‪  34‬ترفندهای پایتون‬

‫در قطعه پد باال‪ ،‬در زمان مناسب خطاهای ‪ AuthError‬و ‪ ValueError‬نمرایی داده‬
‫خواهند شد و شروط مورد نظر به خوبغ پنترل خواهند شد‪.‬‬

‫‪ .6-2-2‬هشدار دوم – ‪ assert‬هایی که همیشه درست هستند‬


‫جای تعزب است اما هنگامغ په به صرورت تصرادفغ عبرارت ‪ assert‬را در پرایتون‬
‫بنویسمد همواره به درستغ ارزیابغ مغشود‪ .‬من در گذشته به دلمل این اشتباه زمان زیادی‬
‫را هدر دادم‪ .‬به طور خالصه مشال به صورت زیر است‪.‬‬
‫زمانغ په یک ‪ Tuple‬را به عنوان اولمن پرارامتر عبرارت ‪ assert‬وارد مغپنمرد‪ ،‬ایرن‬
‫عبارت همواره صحمح درنظر گرفته مغشود‪ .‬بررای مثرال قطعره پرد زیرر همچگراه خطرا‬
‫نخواهد داد‪.‬‬

‫)'‪assert(1 == 2, 'This should fail‬‬

‫این مشال به این دلمل است په عبارات ‪ tuple‬پره خرالغ نمسرتند هممشره بره عنروان‬
‫‪ true‬در زبان پایتون درنظر گرفته مغشوند‪ .‬زمانغ په شما یک ‪ tuple‬رمرر خرالغ را بره‬
‫عبارت ‪ assert‬در پایتون مغدهمد‪ ،‬پاری پامالد بغ فایده انزرام دادهایرد‪ ،‬زیررا همچگراه‬
‫خطایغ تولمد نخواهد شد و حاصل عبارت همواره صحمح است‪.‬‬
‫یک مثال دیگر را باهمدیگر مغبمنمم‪ .‬فرض پنمد مرن برا خوشرحالغ تعردادی تسرت‬
‫نوشته ام و حس ایمنغ پاذبغ را در پدهایم ایزاد پررده ام‪ .‬تصرور پنمرد از ‪assertion‬‬
‫زیر در یاغ از تست ها استفاده پرده ام‪.‬‬

‫( ‪assert‬‬
‫‪counter == 10,‬‬
‫'‪'It should have counted all the items‬‬
‫)‬

‫در نگاه اول تست پامالد درست به نظر مغرسد‪ .‬برا ایرن حرال‪ ،‬هرگرز نتمزره درسرتغ‬
‫نخواهد داشت‪ .‬مقدار ‪ assertion‬همواره برابر با ‪ True‬ارزیابغ مغشود‪ .‬چرا ایرن اتفراق‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪35 ‬‬

‫مغافتد؟ زیرا ‪ assert‬ارزیابغ مغپند پره آیرا ‪ tuple‬دارای مقردار اسرت یرا نره و از آن‬
‫جایغ په دارای مقدار است‪ ،‬پس نتمزه ارزیابغ همواره ‪ True‬است‪.‬‬
‫همانطور په گفتم‪ ،‬زمانغ په متوجه این حقمقت شدم ممخواستم خودپشغ پنم!‬
‫روشغ په مغتوانمد با این مشال مقابله پنمرد اسرتفاده از ‪ code linter‬اسرت‪ .‬نسرخه‬
‫های جدید پایتون نمز دارای پمام اخطار برای این روشِ استفاده از ‪ assertion‬هستند‪.‬‬
‫به هرحال‪ ،‬همواره دقت پنمد تست های برنامه باید ابتدا شاست بخورند و سپس پد‬
‫مربوط به تست‪ ،‬نوشته شود‪ .‬در رمر این صورت ممارن اسرت دچرار اشرتباهات مهلارغ‬
‫شوید همانند موردی په بررسغ پردیم‪.‬‬

‫‪ Assertion .7-2-2‬در پایتون ‪ -‬خالصه‬


‫با وجرود هشردارهایغ پره بررسرغ پرردیم‪ ،‬مرن فارمرغپنم عبرارت ‪ assertion‬در‬
‫پایتون ابزاری قوی برای رفع مشاالت پد است‪ .‬اما متاسفانه بعضغ از توسعه دهنردگان‬
‫از این عبارات استفاده نمغپنند‪.‬‬
‫یادگرفتن طریقه عملاررد ‪ assertion‬و اسرتفاده از آن در پردهایتان‪ ،‬بهتررین روش‬
‫برای مدیریت آسان پد در بلند مدت است‪ .‬زیرا پردهای یرک برنامره تزراری در بلنرد‬
‫مرردت دچررار پمچمرردگغ هررای زیررادی خواهنررد شررد و مرردیریت آن برردون یررک ابررزار‬
‫آشاارسازی مشال‪ ،‬بسمار دشوار و طاقت فرسا است‪.‬‬
‫با یادگمری و استفاده از ‪ assertion‬مغتوانمد مهارت خود را در زبران برنامره نویسرغ‬
‫پایتون به مرحله بعد ببرید و تبدیل به یک پایتونمست حرفهای شوید‪ .‬مرن مطمرئن هسرتم‬
‫په استفاده از این ویژگغ‪ ،‬ساعت های زیاد از زمان من در هنگام رفع مشرال را نزرات‬
‫خواهد داد‪.‬‬

‫‪ .8-2-2‬نکات کلیدی ‪ Assertion‬در پایتون‬


‫‪ ‬عبارت ‪ assert‬در پایتون یک ابزار رفع اشراال قروی اسرت پره مغتوانرد یرک‬
‫سری شرایط را در برنامه شما چک پند‪.‬‬
‫‪  36‬ترفندهای پایتون‬

‫‪ ‬عبارت ‪ assert‬باید تنها برای توسرعه دهنردگان باشرد و اسرتفاده از ایرن عبرارت‬
‫روش مناسبغ برای مدیریت خطاهای زمان اجرای برنامه نمست‪.‬‬
‫‪ ‬عبارت ‪ assert‬مغتواند به صورت سراسری در تنظممات مفسرر پرایتون رمرفعرال‬
‫شود‪.‬‬

‫‪ .3-2‬عبارت ‪ with‬و ‪ context manager‬در پایتون‬


‫عبارت ‪ with‬در زبان برنامه نویسغ پایتون معموال یک عبارت مرمروز درنظرر گرفتره‬
‫مغشود‪ .‬اما زمانغ په به پشت صحنه نگاه مغپنمد‪ ،‬مغبمنمرد پره هرمک جرادویغ در پرار‬
‫نمست‪ .‬عبارت ‪ with‬در واقع یک ویژگغ جذاب و مفمد است په به شما پمک مغپند‬
‫تا پد پایتونغ تممزتر و خواناتری داشته باشمد‪.‬‬
‫ممان است سؤال پنمد عبارت ‪ with‬برای چه زمان هایغ استفاده مغشرود؟ عبرارت‬
‫‪ with‬زمانغ استفاده مغشود په بخواهمم با استفاده از الگروی اسرتانداردی‪ ،‬بره مردیریت‬
‫منابع به صورت بهمنه بپردازیم‪.‬‬
‫برای روشن تر شدن موضو ‪ ،‬به سراغ مثالغ از پتابخانه ‪ open‬په یارغ از پتابخانره‬
‫های درونغ پایتون است مرغرویم‪ .‬پتابخانره ‪ open‬یارغ از بهتررین پتابخانره هرا بررای‬
‫توضمح یک مثال در رابطه با عبارت ‪ with‬در پایتون است‪.‬‬

‫‪with open('hello.txt', 'w') as f:‬‬


‫)'!‪f.write('hello, world‬‬

‫به طور پلغ توصمه مغشود فایرل هرا را برا اسرتفاده از عبرارت ‪ with‬در زبران برنامره‬
‫نویسغ پایتون باز پنمد‪ .‬این پار باعث مغشود زمانغ په پارِ برنامه با فایل مورد نظرر بره‬
‫اتمام مغرسد‪ ،‬ارتباط برنامه با فایل به صورت خودپار بسته شود‪ .‬در حقمقت زمانغ پره‬
‫از عبارت ‪ with‬استفاده مغپنمد پدهای شما به صرورتغ پره مشراهده مغپنمرد‪ ،‬توسرط‬
‫مفسر پایتون ترجمه مغشود‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪37 ‬‬

‫)'‪f = open('hello.txt', 'w‬‬


‫‪try:‬‬
‫)'‪f.write('hello, world‬‬
‫‪finally:‬‬
‫)(‪f.close‬‬

‫ممان است بگویمد این پد پمغ طوالنغ است و پامالد درست اسرت‪ .‬بره ایرن ناتره‬
‫توجه پنمد په استفاده از عبارت ‪ try ... finally‬روشغ مو ر برای مردیریت خطرا اسرت‪.‬‬
‫برای مثال اگر برای این پار‪ ،‬پدی شبمه به زیر بنویسمد‪ ،‬دچار مشاالتغ خواهمد شد‪.‬‬

‫)'‪f = open('hello.txt', 'w‬‬


‫)'‪f.write('hello, world‬‬
‫)(‪f.close‬‬

‫پماده سازی فوق تضممن نمغپند په هنگام نوشتن در فایرل‪ ،‬اگرر برا خطرایغ مواجره‬
‫شدید پس از آن ارتباط با فایل توسط برنامه بسته شود و ممان است برنامه برا خطاهرای‬
‫متفاوتغ روبرو شود‪ .‬به هممن دلمل اسرت پره در ایرن مواقرع از عبرارت ‪ with‬در پرایتون‬
‫استفاده مغپنمم‪ .‬با استفاده از ‪ with‬پس از اتمام پار برنامره برا منرابع‪ ،‬منرابع بره صرورت‬
‫خودپار آزاد خواهند شد‪.‬‬
‫یک اسرتفاده جالرب از عبرارت ‪ with‬درپرایتون‪ ،‬اسرتفاده از آن هنگرام پمراده سرازی‬
‫پالس درونغ ‪ threading.lock‬است‪.‬‬

‫)(‪some_lock = threading.Lock‬‬

‫‪# Harmful:‬‬
‫)(‪some_lock.acquire‬‬
‫‪try:‬‬
‫‪# Do something...‬‬
‫‪finally:‬‬
‫)(‪some_lock.release‬‬

‫‪# Better:‬‬
‫‪with some_lock:‬‬
‫‪# Do something...‬‬
‫‪  38‬ترفندهای پایتون‬

‫همانطور په در پد باال مشراهده مغپنمرد‪ ،‬بزرای نوشرتن ‪ try ... finally‬مغتروان از‬
‫عبارت ‪ with‬استفاده پرد تا پد بتواند از منابع استفاده پرده و سپس منابع را آزاد پنرد‪.‬‬
‫استفاده از عبارت ‪ with‬باعرث خوانرایغ راحرت پرد هنگرام اسرتفاده از منرابع سمسرتمغ‬
‫مغشررود‪ .‬همچنررمن عبررارت ‪ with‬باعررث مغشررود تررا از برره وجررود آمرردن حفررره امنمتررغ‬
‫جلوگمری شود‪ .‬به دلمل ایناه هرزمان په پار برنامه با منابع تمام شد‪ ،‬منابع را به صورت‬
‫خودپار آزاد مغپند‪.‬‬

‫‪ .1-3-2‬پشتیبانی از عبارت ‪ with‬در اشیا اختصاصی‬


‫اپنررون متوجرره شرردید پرره هررمک چمررز جررادویغ در رابطرره بررا تررابع ‪ open‬یررا پررالس‬
‫‪ threading.Lock‬وجود ندارد و حقمقت این است په این اشما را از طریق عبارت ‪with‬‬
‫در پایتون فراخوانغ مرغپنمم‪ .‬شرما مغتوانمرد همرمن عملاررد را در پرالس هرا و توابرع‬
‫پایتونغ خود با استفاده از ‪ context manager‬ها پماده سازی پنمد‪.‬‬

‫‪ .2-3-2‬آشنایی با ‪ context manager‬در پایتون‬


‫‪ Context manager‬چمست؟ یک پروتال یا رابط است په اشرما برنامره بایرد از آن‬
‫پمروی پنند تا اشما برنامه بتوانند از عبارت ‪ with‬پشتمبانغ پنند‪.‬‬
‫اگر بخواهمم عممقتر بنگریم‪ ،‬برای پماده سازی این قابلمت باید توابع __‪ __enter‬و‬
‫__‪ __exit‬پماده سازی شوند تا عملاردی مشرابه برا ‪ context manager‬ایزراد پنرمم‪.‬‬
‫زبان پایتون دو تابع فوق را هنگرام مواجهره برا چرخره مردیریت منرابع سمسرتم فراخروانغ‬
‫مغپند‪.‬‬
‫بمایمد به این موضو در عمل نگاه پنرمم‪ .‬در ادامره یرک پمراده سرازی سراده از الیره‬
‫انتزاعغ تابع ‪ open‬پایتون نوشته شده است‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪39 ‬‬

‫‪class ManagedFile:‬‬
‫‪def __init__(self, name):‬‬
‫‪self.name = name‬‬

‫‪def __enter__(self):‬‬
‫)'‪self.file = open(self.name, 'w‬‬
‫‪return self.file‬‬

‫‪def __exit__(self, exc_type, exc_val, exc_tb):‬‬


‫‪if self.file:‬‬
‫)(‪self.file.close‬‬

‫پالس ‪ ManagedFile‬از پروتال ‪ context manager‬پمرروی مغپنرد و بره همرمن‬


‫دلمل از عبارت ‪ with‬پشتمبانغ مغپند‪ .‬مشابه برا همران پراری پره در مثرال ‪ open‬انزرام‬
‫دادیم را مغتوانمم با پالسغ په به تازگغ توسعه داده ایم‪ ،‬انزام دهمم‪.‬‬

‫‪>>> with ManagedFile('hello.txt') as f:‬‬


‫)'!‪f.write('hello, world‬‬
‫)'‪f.write('bye now‬‬

‫زبان برنامه نویسغ پایتون زمانغ ترابع __‪ __enter‬را فراخروانغ مغپنرد پره برنامره‬
‫شرو به اجرای پدهای درون قسمت ‪ with‬مغپند و برنامره نمراز بره در اختمرار گررفتن‬
‫منابع دارد‪ .‬هنگامغ په اجررای برنامره در قسرمت ‪ with‬بره پایران مغرسرد‪ ،‬پرایتون ترابع‬
‫__‪ __exit‬را فراخوانغ مغپند تا منابعغ په در اختمار گرفته شده بودند‪ ،‬آزاد شوند‪.‬‬
‫نوشتن یک پالس از جنس ‪ context manager‬تنهرا راه بررای پشرتمبانغ از عبرارت‬
‫‪ with‬در پایتون نمست‪ .‬همانطور په مشاهده پردید پالس ‪ ManagedFile‬تعرداد خرط‬
‫پدهای زیادی داشت و در زبان برنامه نویسغ پایتون هممشه به دنبال ساده ترین و بهترین‬
‫راه حل هستمم‪.‬‬
‫با استفاده از پتابخانه ‪ contextlib‬در هسته زبان برنامه نویسغ پرایتون‪ ،‬مغتروان یرک‬
‫الیه انتزاعغ بر روی پروتال ‪ context manager‬ایزراد پررد‪ .‬ایرن پرار ممارن اسرت‬
‫زندگغ را برای شما راحت تر پند!‬
‫‪  40‬ترفندهای پایتون‬

‫برررای مثررال‪ ،‬در قطعرره پررد زیررر بررا اسررتفاده از یررک ‪ decorator‬پررایتون برره اسررم‬
‫‪ contextmanager‬مغتوان اماان پشتمبانغ از عبارت ‪ with‬را به وجود آورد‪ .‬در اینزرا‬
‫مثال قبلغ پالس ‪ ManagedFile‬را بره صرورت ترابع بازنویسرغ مرغپنمم ترا ببمنرمم ایرن‬
‫تانمک چگونه پار مغپند‪.‬‬

‫‪from contextlib import contextmanager‬‬

‫‪@contextmanager‬‬
‫‪def managed_file(name):‬‬
‫‪try:‬‬
‫)'‪f = open(name, 'w‬‬
‫‪yield f‬‬
‫‪finally:‬‬
‫)(‪f.close‬‬

‫‪>>> with managed_file('hello.txt') as f:‬‬


‫)'!‪f.write('hello, world‬‬
‫)'‪f.write('bye now‬‬

‫در این مثال‪ ،‬تابع ‪ managed_file‬یک ‪ generator‬است په ابتدا منابع را در اختمار‬


‫مغگمرد و پس از آن با استفاده از ‪ yield‬اماان استفاده بررای منرابع را بررای درخواسرت‬
‫پننده فعال مغپند‪ .‬زمانغ په اجرای عبارت ‪ with‬به اتمام برسد‪ generator ،‬شرو بره‬
‫تممزپاری و آزاد پردن منابع مغپند تا اختمار استفاده از منابع به سمستم برگردد‪.‬‬
‫پماده سازی ‪ context manager‬به دو صورت ‪ class-based‬و ‪generator-based‬‬
‫در عمل پامالد مشابه هستند‪ .‬بهتر است شما با هر روشغ په راحت هستمد‪ ،‬از یاغ از این‬
‫دو روش استفاده پنمد‪.‬‬
‫برای درک بهتر پماده سازی ‪ context manager‬در پرایتون بهترر اسرت ابتردا درک‬
‫عممقغ از ‪ decorator‬ها و ‪ generator‬ها در پایتون به دسرت آوریرد‪ .‬اگرر مغخواهمرد‬
‫هممن االن این درک عممق را بدسرت آوریرد مغتوانمرد بره فصرل هرای مربروط بره ایرن‬
‫مباحث مراجعه پنمد‪ .‬همانطور په در ابتردا گفترمم ایرن پتراب هماننرد یرک بوفره پرر از‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪41 ‬‬

‫ویژگغ ها و ترفندهای پایتون است و مغتوانمد خملرغ سرریع بره مباحرث مختلرف گرذر‬
‫پنمد‪.‬‬
‫یک بار دیگر برای تاپمد مغگویم‪ ،‬این په از پدام روش پماده سازی استفاده پنمرد‬
‫پامالد به ایناه شما و تممتان با پدام روش راحت تر است‪ ،‬بستگغ دارد‪ .‬هممشه راهرغ را‬
‫انتخاب پنمد په به خوانایغ پدهایتان افزوده شود‪.‬‬

‫‪ .3-3-2‬نوشتن ‪ API‬های زیبا توسط ‪ context manager‬در پایتون‬


‫‪ Context manager‬ها در پایتون پامالد انعطاف پذیر هستند‪ .‬اگر از عبارت ‪ with‬به‬
‫صورت خالقانهای استفاده پنمد‪ ،‬مغتوانمد ‪ API‬های زیبایغ برای ماژولها و پالسهرای‬
‫خود طراحغ پنمد‪.‬‬
‫برای مثال‪ ،‬اگر منابعغ په مغخواهمم استفاده پنمم مزموعهای از نوشته ها باشند پره‬
‫توسط یک برنامه گزارش گمری تولمد شده اند و مزموعه ای از تو رفتگغ ها (‪)indent‬‬
‫در آن وجود دارد‪ ،‬چطور این پار را انزام مغدهمم؟‬
‫بمشتر توضمح مغدهم‪ ،‬اگر بخواهمد برنامه ای بنویسمد په گزارشرغ بره صرورت مرتن‬
‫تولمد مغپند و این گزارش در هر سطح شامل یک سری تو رفتگغها است‪ ،‬چطور این‬
‫پار را انزام مغدهمد؟ چطور مغتوانستمم یک الیه انتزاعغ پماده سازی پنمم تا این پرار‬
‫را به راحتغ انزام دهمم؟‬

‫‪with Indenter() as indent:‬‬


‫)'!‪indent.print('hi‬‬
‫‪with indent:‬‬
‫)'‪indent.print('hello‬‬
‫‪with indent:‬‬
‫)'‪indent.print('bonjour‬‬
‫)'‪indent.print('hey‬‬

‫پد باال شبمه زبان های ‪ domain-specific‬برای ایزاد تورفتگغ در متنها است‪ .‬بره پرد‬
‫باال دقت پنمد‪ ،‬متوجه مغشوید پره ایرن پرد چنردین برار وارد ‪ context manager‬هرای‬
‫‪  42‬ترفندهای پایتون‬

‫یاسان شده و از آن خارج شده است تا تورفتگغ ها را اعمال پند‪ .‬اجرا پد باال باید نتمزهای‬
‫مشابه زیر را در پنسول برای شما چاپ پند‪.‬‬
‫!‪hi‬‬
‫‪hello‬‬
‫‪bonjour‬‬
‫‪hey‬‬

‫بنرابراین‪ ،‬برگرردیم بره اصرل موضرو ‪ ،‬چطرور یرک ‪ context manager‬مغسرازید ترا‬
‫عملارد باال را بتوانمد ایزاد پنمد؟‬
‫به هرحال‪ ،‬این مغتواند تمرین خوبغ باشد تا به درک عممقغ از ‪ context manager‬ها‬
‫در پایتون برسمد‪ .‬قبل از ایناه پماده سازی من را چک پنمد‪ ،‬مقداری زمان بگذارید و سرعغ‬
‫پنمد یک پماده سازی انتزاعغ به عنوان تمرین برای پد باال انزام دهمد‪.‬‬
‫اگر آماده هستمد په پماده سازی من را ببمنمد‪ ،‬این روشرغ اسرت پره مرن بررای پمراده‬
‫سازی یک ‪ context manager‬به صورت ‪ class-based‬انزام مغدهم تا بتوانم قابلمت‬
‫ایزاد تورفتگغ را در پدهایم را به صورت یک ‪ API‬ایزاد پنم‪.‬‬

‫‪class Indenter:‬‬
‫‪def __init__(self):‬‬
‫‪self.level = 0‬‬

‫‪def __enter__(self):‬‬
‫‪self.level += 1‬‬
‫‪return self‬‬

‫‪def __exit__(self, exc_type, exc_val, exc_tb):‬‬


‫‪self.level -= 1‬‬

‫‪def print(self, text):‬‬


‫)‪print(' ' * self.level + text‬‬

‫زیاد بد نمسرت‪ ،‬درسرت اسرت؟ اممردوارم برا خوانردن ایرن قسرمت‪ ،‬درک بهترری از‬
‫‪ context manager‬ها و عبارت ‪ with‬در برنامه های پایتونغ بدست آوریرد‪ .‬ایرن یرک‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪43 ‬‬

‫ویژگغ جذاب در پایتون است په با آن مغتوانمد منابع سمستم را پایتونمک ترر مردیریت‬
‫پنمد‪.‬‬

‫‪ .4-3-2‬نکات کلیدی عبارت ‪ with‬در پایتون‬


‫‪ ‬استفاده از عبارت ‪ with‬باعث مغشود ترا بتوانمرد راحرت ترر خطاهرای برنامره را‬
‫مدیریت پنمرد زیررا برا ایرن پرار عملمرات ‪ encapsulation‬را برر روی عبرارت‬
‫استاندارد ‪ try...finally‬انزام مغدهمد‪.‬‬
‫‪ ‬بمشترین استفاده از عبارت ‪ with‬زمرانغ اسرت پره مغخواهمرد منرابعغ را توسرط‬
‫برنامه پایتونغ در اختمار بگمرید و پس از انزام پار‪ ،‬منابع را به صورت خودپرار‬
‫آزاد پنمد‪.‬‬
‫‪ ‬استفاده از عبارت ‪ with‬باعث جلوگمری از نشت منرابع خواهرد شرد و همچنرمن‬
‫خوانایغ بهتری به پدهای شما اضافه مغپند‪.‬‬

‫‪ .4-2‬آندرالین و داندرالین در پایتون‬


‫استفاده از عالمت _‪ 1‬و عالمت __‪ 2‬در نام متغمرها و توابرع پرایتونغ معرانغ متفراوتغ‬
‫دارد‪ .‬گاهغ اوقات برای راهنمایغ به توسعه دهندگان دیگری په با پرد سرروپار دارنرد‬
‫است و گاهغ به دلمل اجبار مفسر پایتون این پار را انزام مغدهمم‪.‬‬
‫اگر تعزب پرده اید پره اسرتفاده از عالمتهرای _ و __ در پرایتون معرانغ مختلفرغ‬
‫دارند‪ ،‬من در اینزا سعغ پردهام به بهترین شال به این ابهام پاس دهم‪ .‬در این قسرمت‪،‬‬
‫در رابطه با پنج حالت مختلف زیر توضمح خواهمم داد و ایناه هرپدام از این مروارد در‬
‫زبان برنامه نویسغ پایتون چه مفهومغ دارند‪.‬‬
‫‪ Single Underscore ‬در ابتدا‪_var :‬‬
‫‪ Single Underscore ‬در انتها‪var_ :‬‬

‫‪1 Single Underscore‬‬


‫‪2 Double Underscore‬‬
‫‪  44‬ترفندهای پایتون‬

‫‪ Double Underscore ‬در ابتدا‪__var :‬‬


‫‪ Double Underscore ‬در ابتدا و انتها‪__var__ :‬‬
‫‪ Single Undercorse ‬به تنهایغ‪_ :‬‬

‫‪ Single Underscore .1-4-2‬در ابتدا ‪_var :‬‬

‫زمانغ په عالمت _ به صورت پمشروند در ابتردای نرام متغمرهرا و توابرع پرایتون قررار‬
‫مغگمرد‪ ،‬فقط به دلمل یک قرارداد بمن برنامره نویسران پرایتون اسرت‪ .‬درواقرع ایرن یرک‬
‫قرارداد یا رسم‪ ،‬بمن برنامه نویسان جامعه پایتون است و ترا مری در نحروه اجررای برنامره‬
‫شما ندارد‪.‬‬
‫استفاده از پمشوند _ در ابتدای نام متغمرها و توابع پایتون‪ ،‬تنها برای راهنمرایغ توسرعه‬
‫دهندگان دیگر است و به این مفهوم است په متغمر یا تابع مورد نظر برای استفاده درونغ‬
‫در پدها در نظر گرفته شده است‪ .‬این یک قرارداد است په در ‪ PEP 8‬پرایتون تعریرف‬
‫شده است و در استایل پدنویسغ پایتون رعایت مغشود‪.‬‬
‫هرچند‪ ،‬این قرارداد توسط مفسر پایتون به برنامه نویسان اجبار نمغشود‪ .‬پایتون بررای‬
‫متغمرهای ‪ private‬و ‪ public‬همانند زبان برنامه نویسغ جاوا تفاوت قائل نمغشود‪.‬‬
‫با قرار دادن _ در ابتدای نام‪ ،‬دارید به توسرعه دهنرده دیگرری پره در حرال خوانردن‬
‫پدهای شماست مغگویمد‪ :‬هِغ‪ ،‬من این متغمر را فقط برای استفاده درونغ درنظر گرفتره‬
‫ام و بخشغ از الیه عمومغ پد نمست‪ ،‬بهتره باهاش پاری نداشته باشغ‪.‬‬
‫به مثال زیر نگاه پنمد‪.‬‬

‫‪class Test:‬‬
‫‪def __init__(self):‬‬
‫‪self.foo = 11‬‬
‫‪self._bar = 23‬‬

‫اگر از این پالس یک نمونره جدیرد بسرازید آیرا مغتوانمرد بره مقرادیر ‪ foo‬و ‪_bar‬‬
‫دسترسغ پمدا پنمد؟ بمایمد امتحان پنمم‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪45 ‬‬

‫)(‪>>> t = Test‬‬
‫‪>>> t.foo‬‬
‫‪11‬‬
‫‪>>> t._bar‬‬
‫‪23‬‬

‫همررانطور پرره مشرراهده مغپنمررد‪ ،‬اسررتفاده از عالمررت _ در متغمررر ‪ _bar‬باعررث عرردم‬


‫دسترسغ به متغمر مورد نظرر نشرد و توانسرتمم بره آن دسترسرغ پمردا پنرمم و مقردارش را‬
‫نمایی دهمم‪.‬‬
‫این موضو به این دلمل است په استفاده از عالمت _ در ابتدای نام متغمرهرا و توابرع‬
‫تنها یک توافق در جامعه پایتونغ است و یک اجبار نمست‪.‬‬
‫با این حال‪ ،‬اگرر از عالمرت _ در ابتردای نرام توابرع اسرتفاده مغپنمرد‪ ،‬هنگرامغ پره‬
‫مغخواهمد یک ماژول را فراخوانغ پنمد‪ ،‬توابعغ په در ابتدای نام آنها عالمرت _ اسرت‬
‫فراخوانغ نمغشوند‪ .‬همانند مثال زیر په یک ماژول به اسم ‪ my_module‬نوشتهایم‪.‬‬

‫‪# my_module.py:‬‬
‫‪def external_func():‬‬
‫‪return 23‬‬
‫‪def _internal_func():‬‬
‫‪return 42‬‬

‫اپنون‪ ،‬به دلمل ایناه در ابتدای نام تابع عالمت _ قرار دادهایم‪ ،‬اگر تمام توابرع را در‬
‫اساریپت دیگری ‪ import‬پنمم‪ ،‬پایتون توابعغ په در ابتدای نام آنها عالمرت _ اسرت‬
‫را در اساریپت ‪ import‬نمغپند‪.‬‬

‫* ‪>>> from my_module import‬‬


‫)(‪>>> external_func‬‬
‫‪23‬‬
‫)(‪>>> _internal_func‬‬
‫"‪NameError: "name '_internal_func' is not defined‬‬

‫به این طریقهی ‪ ،import‬اصرالحا ‪ wildcard import‬گفتره مغشرود و بایرد ترا حرد‬
‫اماان از آن اجتناب پرد‪ .‬زیرا با این پار مشخص نمست چه ‪ namespace‬هرایغ درون‬
‫‪  46‬ترفندهای پایتون‬

‫برنامه پایتون وارد مغشوند‪ .‬بهتر است برای خوانایغ بمشتر پد‪ ،‬بره صرورت دقمرق مروارد‬
‫‪ import‬ها مشخص شوند‪ .‬هنگرامغ پره بره صرورت عرادی یرک مراژول را بره تنهرایغ‬
‫‪ import‬مغپنمد‪ ،‬قاعده فوق در توابعغ پره در ابتردای نرام آنهرا عالمرت _ قررار دارد‬
‫رعایت نمغشود و مغتوانمد توابع را فراخوانغ پنمد‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫‪>>> import my_module‬‬


‫)(‪>>> my_module.external_func‬‬
‫‪23‬‬
‫)(‪>>> my_module._internal_func‬‬
‫‪42‬‬

‫مغدانم په ممان است در این جا پمغ گمج شده باشمد‪ .‬اگر استاندارد توصمه شرده‬
‫استایل پدنویسغ ‪ PEP 8‬را رعایت پنمد و از ‪ wildcard import‬ها اجتناب پنمرد‪ ،‬تنهرا‬
‫پافغ است یک چمز را به یاد داشته باشمد‪:‬‬
‫استفاده از ‪ single underscore‬در ابتردای نرام متغمرهرا و توابرع پرایتونغ فقرط یرک‬
‫قرارداد بمن توسعه دهندگان به معنغ استفاده داخلغ متغمر یا ترابع اسرت‪ .‬عردم فراخروانغ‬
‫این متغمرها و توابع توسط مفسر پرایتون اجبرار نمغشرود و تنهرا بررای راهنمرایغ توسرعه‬
‫دهندگان دیگر به منظور استفاده داخلغ متغمر یا تابع است‪.‬‬

‫‪ Single Underscore .2-4-2‬در انتها‪var_ :‬‬

‫گاهغ اوقات بهترین اسمغ په برای یک متغمر مغتوانمم انتخاب پنرمم توسرط هسرته‬
‫زبان برنامه نویسغ پایتون انتخاب شده است و نمغتوانمم از آن اسم استفاده پنرمم‪ .‬بررای‬
‫مثال اسم هایغ نظمر ‪ class‬یا ‪ def‬را نمغتوانمم برای یک متغمر پایتونغ انتخاب پنرمم‪ .‬در‬
‫این مواقع مغتوانمد از یک عالمت _ در انتهای نام متغمر استفاده پنمد تا جلروی ترداخل‬
‫نامها را بگمرید‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪47 ‬‬

‫‪>>> def make_object(name, class):‬‬


‫"‪SyntaxError: "invalid syntax‬‬

‫‪>>> def make_object(name, class_):‬‬


‫‪pass‬‬

‫به صورت خالصه‪ ،‬استفاده از عالمت _ در انتهای نام متغمرهرای پرایتونغ‪ ،‬بره معنرای‬
‫جلوگمری از تداخل نام متغمر با نام های رزرو شده در زبان پایتون است و ایرن موضرو‬
‫در راهنمای استایل پدنویسغ ‪ PEP 8‬نوشته شده است‪.‬‬

‫‪ Double Underscore .3-4-2‬در ابتدا‪__var :‬‬

‫الگوهایغ از نام گذاری پره تراپنون بررسرغ پرردیم‪ ،‬ارلرب قرراردادی برمن توسرعه‬
‫دهندگان بودند‪ .‬در رابطه با پالس های پرایتونغ پره ویژگغهرا و رفتارهرای پرالس برا‬
‫عالمت __ شرو مغشود‪ ،‬قضمه پمغ متفاوت است‪.‬‬
‫زمانغ په از عالمت __ در ابتدای نام متغمرها و توابع یک پالس اسرتفاده مغپنمرد‪،‬‬
‫مفسر پایتون نام این صفات را تغممر مغدهد تا از تداخل نامها جلوگمری پند‪ .‬به این پار‬
‫در پایتون ‪ name mangling‬مغگویند‪ .‬مفسر پرایتون نرام صرفت را تغممرر مغدهرد ترا از‬
‫ایزاد تداخل نامها در آینده جلوگمری پند‪ .‬درواقع با این پرار هنگرام گسرترش پردها‪،‬‬
‫احتمال ایزاد تداخل نامهای یک پالس پمتر مغشود‪.‬‬
‫من مغدانم په این یک موضو انتزاعغ در پایتون است و به این دلمل قطعه پد زیرر‬
‫را نوشتم تا بهتر بتوانمم راجع به طرز پار مفسر پایتون صحبت پنمم‪.‬‬

‫‪class Test:‬‬
‫‪def __init__(self):‬‬
‫‪self.foo = 11‬‬
‫‪self._bar = 23‬‬
‫‪self.__baz = 42‬‬

‫در قطعه پد باال یک پالس ساده ی پایتونغ نوشتمم‪ .‬اپنون بمایمد برا اسرتفاده از ترابع‬
‫‪ dir‬په از توابع درونغ زبان برنامه نویسغ پایتون است به صفت های این شغ نگاه پنمم‪.‬‬
‫‪  48‬ترفندهای پایتون‬

‫)(‪>>> t = Test‬‬
‫)‪>>> dir(t‬‬
‫‪['_Test__baz', '__class__', '__delattr__','__dict__',‬‬
‫‪'__dir__', '__doc__', '__eq__', '__format__',‬‬
‫‪'__ge__',‬‬
‫‪'__getattribute__', '__gt__', '__hash__','__init__',‬‬
‫‪'__le__', '__lt__', '__module__', '__ne__',‬‬
‫‪'__new__',‬‬
‫‪'__reduce__', '__reduce_ex__', '__repr__',‬‬
‫‪'__setattr__', '__sizeof__', '__str__',‬‬
‫]'‪'__subclasshook__', '__weakref__', '_bar', 'foo‬‬

‫با استفاده از این تابع مغتوانمم لمستغ از نام ویژگغهای یرک شرغ را مشراهده پنرمم‪.‬‬
‫بمایمد به دنبال نام های ‪ _bar ،foo‬و ‪ __baz‬پره بره ترازگغ در شرغ (یرا پرالس) ‪Test‬‬
‫ساختمم بگردیم‪ .‬آیا مغتوانمد هر سه صفت را در لمست صفت هرای شرغ ‪ t‬پمردا پنمرد؟‬
‫مطمئنم هستم تغممرات عزمبغ مشاهده مغپنمد!‬
‫اول از همه‪ ،‬صفت ‪ self.foo‬بدون تغممر و با نام ‪ foo‬په برای آن مشخص پردیم در‬
‫لمست خروجغ ظاهر شده است‪.‬‬
‫بعد از ‪ ،foo‬به دنبال صفت ‪ self._bar‬مغگردیم و مشاهده مغپنمم په همانند صفت‬
‫قبلغ رفتار پرده است و به صورت نام ‪ _bar‬قابل مشاهده است‪ .‬همانطور په قبال گفتمم‬
‫عالمت _ تنها یک راهنما برای برنامه نویسان دیگر است‪.‬‬
‫به هر حال‪ ،‬به سراغ ‪ self.__baz‬مرغرویم امرا ظراهرا نتمزره پمرغ متفراوت بره نظرر‬
‫مغرسد‪ .‬زمانغ په پلمه ‪ __baz‬را در لمست جسرت و جرو مرغپنمم‪ ،‬مشراهده مغشرود‬
‫همک پلمه ای برابر با این نام وجود ندارد‪.‬‬
‫پس چه اتفاقغ برای ‪ __baz‬افتاد؟‬
‫اگر پمغ دقمقتر نگاه پنمد‪ ،‬نام ‪ _Test__baz‬را در خروجغ مشاهده مغپنمرد‪ .‬ایرن‬
‫همان ‪ name mangling‬است په پمیتر گفتمم‪ .‬مفسر پایتون بره صرورت خودپرار نرام‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪49 ‬‬

‫پالس را در ابتدای نام صفت قرار مغدهد تا از نام این صفت در آینده هنگام گسرترش‬
‫پدها محافظت پند‪.‬‬
‫اپنون شاید از خود بپرسمد مفسر پایتون چرا باید از نام این پالس در آینده محافظت‬
‫پند؟ این پار برای محافظت از نرام ویژگغهرای یرک شرغ در مقابرل ‪ override‬شردن‬
‫توسط پالس های فرزندی است په در آینده قرار است ساخته شوند‪.‬‬
‫برای روشن تر شدن موضو بمایمد یک پالس دیگر بسازیم و آن را از پالس ‪Test‬‬
‫ارث بررری پنررمم و سررعغ پنررمم ویژگغهررای پررالس ‪ Test‬را ‪ override‬پنررمم‪ .‬بنررابراین‬
‫پالس جدید را به شال زیر مغنویسم‪.‬‬

‫‪class ExtendedTest(Test):‬‬
‫‪def __init__(self):‬‬
‫)(__‪super().__init‬‬
‫'‪self.foo = 'overridden‬‬
‫'‪self._bar = 'overridden‬‬
‫'‪self.__baz = 'overridden‬‬

‫اپنون فار مغپنمد مقادیر صفتهای ‪ bar_ ،foo‬و ‪ __baz‬در یرک شرغ از جرنس‬
‫پالس ‪ ExtendedTest‬چه مقادیری خواهد بود؟‬

‫)(‪>>> t2 = ExtendedTest‬‬
‫‪>>> t2.foo‬‬
‫'‪'overridden‬‬
‫'‪>>> t2._bar 'overridden‬‬
‫‪>>> t2.__baz‬‬
‫\ ‪AttributeError:‬‬
‫"'‪"'ExtendedTest' object has no attribute '__baz‬‬

‫صبرپنمد! چه اتفاقغ افتاد؟ چررا هنگرامغ پره خواسرتمم مقردار صرفت ‪ t2.__baz‬را‬
‫بدست آوریم یک خطای ‪ AttributeError‬گرفتمم؟ ظاهرا ‪ name mangling‬مثل قبرل‬
‫دست به پار شده است! همانطور په مشاهده مغپنمد همانند قبل خروجغ صفحهی بعد‬
‫شامل نام صفت ‪ __baz‬نمست‪.‬‬
‫ ترفندهای پایتون‬ 50

>>> dir(t2)
['_ExtendedTest__baz', '_Test__baz', '__class__',
'__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__',
'__getattribute__',
'__gt__', '__hash__', '__init__', '__le__',
'__lt__',
'__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__',
'__weakref__', '_bar', 'foo', 'get_vars']

‫__ برای جلوگمری از ترداخل تصرادفغ تبردیل بره‬baz ‫ نام‬،‫همانطور په واضح است‬
‫_ هنروز در لمسرت فروق وجرود‬Test__baz ‫_ شده است اما نام‬ExtendedTest__baz
.‫دارد و هرپدام دارای مقادیر مختلفغ هستند‬

>>> t2._ExtendedTest__baz
'overridden'
>>> t2._Test__baz
42

‫ در هنگام استفاده‬name mangling ‫بسماری از برنامه نویسان پایتون از نحوه عملارد‬


‫ مثال زیرر ایرن موضرو را‬.‫از عالمت __ در نام ویژگغهای یک پالس اطالعغ ندارند‬
.‫تایمد مغپند‬

class ManglingTest:
def __init__(self):
self.__mangled = 'hello'

def get_mangled(self):
return self.__mangled

>>> ManglingTest().get_mangled()
'hello'
>>> ManglingTest().__mangled
AttributeError: \
"'ManglingTest' object has no attribute '__mangled'"
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪51 ‬‬

‫آیا ‪ name mangling‬روی نام توابرع هرم اعمرال مغشرود؟ بلره‪name mangling .‬‬
‫درتمام نام های توابع یک پالس په با عالمت __ شرو مغشوند‪ ،‬اعمال مغشود‪.‬‬

‫‪class MangledMethod:‬‬
‫‪def __method(self):‬‬
‫‪return 42‬‬

‫‪def call_it(self):‬‬
‫)(‪return self.__method‬‬

‫)(‪>>> MangledMethod().__method‬‬
‫\ ‪AttributeError:‬‬
‫"'‪"'MangledMethod' object has no attribute '__method‬‬
‫)(‪>>> MangledMethod().call_it‬‬
‫‪42‬‬

‫یک مثال دیگر را مغبمنمم په احتماال شما را شگفت زده مغپند‪.‬‬

‫‪_MangledGlobal__mangled = 23‬‬

‫‪class MangledGlobal:‬‬
‫‪def test(self):‬‬
‫‪return __mangled‬‬
‫)(‪>>> MangledGlobal().test‬‬
‫‪23‬‬

‫در این مثال‪ ،‬ابتردا یرک متغمرر سراسرری ‪ _MangledGlobal__mangled‬تعریرف‬


‫پرده ام‪ .‬سپس به این متغمر از طریق پالس ‪ MangledGlobal‬دسترسغ پمدا پرده ام‪ .‬به‬
‫دلمررررل اسررررتفاده از قابلمررررت ‪ name mangling‬در پررررایتون‪ ،‬توانسررررتم متغمررررر‬
‫‪ _MangledGlobal__mangled‬را از طریق نام ‪ __mangled‬در ترابع ‪ test‬فراخروانغ‬
‫پنم‪.‬‬
‫مفسرررررر پرررررایتون بررررره صرررررورت خودپرررررار نرررررام ‪ __mangled‬را بررررره نرررررام‬
‫‪ _MangledGlobal__mangled‬تغممر داد‪ .‬به این دلمل پره ایرن صرفت برا عالمرت __‬
‫شرو شده است‪ .‬این موضو مشرخص مغپنرد پره ‪ name mangling‬نره تنهرا در نرام‬
‫‪  52‬ترفندهای پایتون‬

‫متغمرها و توابع‪ ،‬بلاه در هر نامغ په با عالمت __ در فضرای یرک پرالس شررو شرود‬
‫توسط مفسر پایتون اعمال مغشود‪.‬‬
‫ظاهرا خملغ عممق شدیم‪ .‬جالب است نه؟ حقمقت این است پره مرن ایرن مثرال هرا و‬
‫توضمحات را خملغ راحت از ذهن خود در نماورده ام‪ .‬زمان و انرژی زیادی برای تحقمق‬
‫و درک درستغ از این ویژگغ از دست داده ام‪ .‬من چندین سال است په از زبان پایتون‬
‫برای برنامهنویسغ استفاده مغپنم‪ .‬اما درک نحوه عملارد مفسر پایتون در حالرت هرای‬
‫نماز به تحقمقات فراوانغ دارد‪.‬‬ ‫خا‬
‫گاهغ اوقات مهم ترین مهارت یک برنامه نویس‪ ،‬تشخمص الگو و پمدا پردن جرایغ‬
‫است په باید مشال را در آنزا پمدا پند‪ .‬اگر احساس مغپنمد پمغ در درک این مثال‬
‫ها گمج شده اید‪ ،‬نگران همک چمز نباشمد‪ .‬پمغ به خود زمان بدهمد و برا مثرال هرای ایرن‬
‫قسمت تمرین پنمد تا به خوبغ متوجه شوید‪.‬‬

‫‪ .4-4-2‬نکته جانبی‪ :‬داندر‪ 1‬در پایتون چیست؟‬


‫اگر گاهغ صحبت پایتونمست های مختلف راجع به زبان پایتون را گوش مغدهمد یرا‬
‫پنفرانس های پایاان را مشاهده مغپنمد ممان است پلمه ی داندر به گوشتان خرورده‬
‫باشد و تعزب پرده باشمد داندر به چه معنایغ است‪.‬‬
‫‪ Double underscore‬ها (عالمت __) گاهغ اوقرات بره دلمرل راحترغ در صرحبت‬
‫پردن‪ ،‬توسط جامعه پایتون ‪ dunder‬تلفظ مغشوند‪ .‬به این دلمل په استفاده از ‪double‬‬
‫‪ underscore‬ها در پدهای پایتونغ زیاد اتفاق مغافتد و بررای ایناره توسرعه دهنردگان‬
‫پررایتون از خسررته شرردن ماهمچرره هررای فررک خررود جلرروگمری پننررد‪ ،‬ارلررب ‪double‬‬
‫‪ underscore‬را به حالت پوتاه شده ‪ dunder‬تلفظ مغپنند‪.‬‬

‫‪1 Dunder‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪53 ‬‬

‫برای مثال من نام ‪ __baz‬را ‪ dunder baz‬مغخوانم‪ .‬همچنمن نرام __‪ __init‬را نمرز‬
‫‪ dunder init‬مغخوانم‪ .‬اگرچه ممان است بعضغ افراد فار پنند باید آن را به صورت‬
‫‪ dunder init dunder‬بخوانند‪.‬‬
‫خب برویم سراغ نحوه نام گذاری بعدی‪ ،‬یک قاعده نام گذاری دیگری وجود دارد‬
‫په برای پایتونمست ها شبمه به اسم رمز مغماند‪.‬‬

‫‪ double underscore .5-4-2‬در ابتدا و انتها‪__var__ :‬‬

‫ممان است باعث شگفت زدگغ شما شود اما ‪ name mangling‬در زمانغ په یرک‬
‫صفت پایتونغ با ‪ dunder‬شرو مغشود و پایان مغیابد‪ ،‬اعمال نمغشود‪ .‬متغمرهایغ پره‬
‫در ابتدا و انتهای آنها ‪ dunder‬قرار دارد توسط مفسر پایتون دست پاری نمغشوند‪.‬‬

‫‪class PrefixPostfixTest:‬‬
‫‪def __init__(self):‬‬
‫‪self.__bam__ = 42‬‬
‫__‪>>> PrefixPostfixTest().__bam‬‬
‫‪42‬‬

‫نام هایغ په در پایتون عالمت __ در ابتدا و انتهای آنها وجود دارد‪ ،‬بررای اسرتفاده‬
‫در زبرران برنامرره نویسررغ پررایتون طراحررغ شررده انررد‪ .‬ایررن قررانون در ترروابعغ مثررل‬ ‫خررا‬
‫__‪ __init‬په تابع سازنده یک پالس است یا تابع __‪ __call‬په برای فراخروانغ شرغ‬
‫استفاده مغشود یا توابع دیگر باار گرفته شده است‪.‬‬
‫این توابع به عنوان ‪ magic method‬یا متدهای جادویغ توسط پرایتون معرفرغ شرده‬
‫اند‪ ،‬اما بسماری از افراد جامعه پرایتون از جملره خرودم نرام متردهای جرادویغ را دوسرت‬
‫نداریم‪ .‬این نام باعث مغشود بسماری از برنامه نویسان از اسرتفاده از ایرن قابلمرت دلسررد‬
‫شوند‪ .‬در صورتغ په همک چمز جادویغ در این رابطه وجود ندارد‪ .‬این یارغ از ویژگرغ‬
‫های هسته زبان برنامه نویسغ پایتون است په هر زمان نمراز باشرد‪ ،‬بایرد بتوانمرد از آنهرا‬
‫استفاده پنمد‪.‬‬
‫‪  54‬ترفندهای پایتون‬

‫به هر حال‪ ،‬بهتر است متغمرهایغ په با عالمت __ شرو و پایان مغیابند نسازید تا از‬
‫تداخل پدهایتان در تغممرات آینده هسته زبان برنامه نویسغ پایتون جلوگمری پنمد‪.‬‬

‫‪ Single Undercorse .6-4-2‬به تنهایی‪_ :‬‬


‫بر اساس قراردادی‪ ،‬یرک عالمرت _ در زبران پرایتون هنگرامغ اسرتفاده مغشرود پره‬
‫مغخواهمم برای یک متغمر یک نام موقتغ در نظر بگمریم‪.‬‬
‫برای مثال‪ ،‬در حلقه زیر نمازی به شمارنده حلقه نداریم و مغتروانمم از یرک عالمرت‬
‫"_" برای نام شمارنده حلقه استفاده پنمم‪.‬‬

‫‪>>> for _ in range(32):‬‬


‫)'‪print('Hello, World.‬‬

‫همچنمن شما مغتوانمد از عالمت _ برای نام گذاری متغمرهرایغ پره برایتران اهممرت‬
‫ندارند هنگرام براز پرردن یرک پالاشرن پرایتون اسرتفاده پنمرد‪ .‬تاپمرد مرغپنم‪ ،‬ایناره‬
‫مغگویم متغمر اهممت ندارد تنهرا یرک رسرم برمن جامعره پرایتونغ اسرت‪ .‬مفسرر پرایتون‬
‫هنگامغ په با "_" روبرو مغشود همک تغممری در نحوه ی اجرای برنامه انزام نمغدهرد‪.‬‬
‫مغتوانمد اینطور در نظر بگمرید په "_" تنها یک متغمر معتبر است په برای مواقعغ پره‬
‫نام متغمر برایمان اهممت ندارد از این نام استفاده مغپنمم‪.‬‬
‫برای مثال در پد زیر‪ ،‬من یک ‪ tuple‬را باز مرغپنم پره تنهرا عالقمنرد بره فملردهای‬
‫‪ color‬و ‪ mileage‬پالاشن ‪ tuple‬هستم و بقمه فملدها برایم مهم نمستند‪ .‬به هر حرال‪ ،‬بره‬
‫دلمل ایناه ترتمب هنگام باز پردن یک پالاشن مهم اسرت متغمرهرا را بره ترتمرب قررار‬
‫مغدهم و اینزا جایغ است په نام "_" به درد من مغخورد‪.‬‬

‫)‪>>> car = ('red', 'auto', 12, 3812.4‬‬


‫‪>>> color, _, _, mileage = car‬‬

‫‪>>> color‬‬
‫'‪'red‬‬

‫‪>>> mileage‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪55 ‬‬

‫‪3812.4‬‬

‫_ >>>‬
‫‪12‬‬

‫در پنار ایناه "_" برای نام گذاری متغمرهرای مروقتغ اسرتفاده مغشرود‪ "_" ،‬یرک‬
‫در ‪ Python REPL‬است و از طریق آن مغتوان به نتمزه محاسبات قبلرغ‬ ‫متغمر مخصو‬
‫در ‪ interpreter‬پایتون دسترسغ داشت‪.‬‬
‫برای مثال هنگامغ په با سشن مفسر پایتون در حال پار هستمد و نماز به دسترسرغ بره‬
‫نتمزه محاسبه قبلغ را دارید مغتوانمد از "_" استفاده پنمد‪.‬‬

‫‪>>> 20 + 3‬‬
‫‪23‬‬
‫_ >>>‬
‫‪23‬‬
‫)_(‪>>> print‬‬
‫‪23‬‬

‫همچنمن اگر مغخواهمد یک شغ مروقتغ بسرازید (اصرلطالحا ‪)objects on the fly‬‬


‫مغتوانمد از "_" استفاده پنمد تا نامغ برای شغ خود انتخاب نانمد‪.‬‬

‫>>>‬ ‫)(‪list‬‬
‫][‬
‫>>>‬ ‫)‪_.append(1‬‬
‫>>>‬ ‫)‪_.append(2‬‬
‫>>>‬ ‫)‪_.append(3‬‬
‫>>>‬ ‫_‬
‫‪[1,‬‬ ‫]‪2, 3‬‬

‫‪ .7-4-2‬نکات کلیدی آندرها و داندرها در پایتون‬


‫‪ Single underscore ‬در ابتدا‪ :‬یک رسم نام گذاری برای اطال برنامره نویسران‬
‫دیگر از درونغ یا محلغ بودن متغمر است‪ .‬مفسر همک اجباری برای محلرغ برودن‬
‫متغمر انزام نمغدهد‪.‬‬
‫‪  56‬ترفندهای پایتون‬

‫‪ Single underscore ‬در انتها‪ :‬یک رسم نام گذاری برای جلروگمری از ترداخل‬
‫نام با پلمات پلمدی زبان برنامه نویسغ پایتون است و توسط مفسر پایتون پاری‬
‫انزام نمغشود‪.‬‬
‫‪ Double underscore ‬در ابتدا‪ :‬باعث اعمال ‪ name mangling‬در سرطح یرک‬
‫پالس مغشود و توسط مفسر پایتون تغممراتغ در نام گذاری انزام مغشود‪.‬‬
‫‪ Double underscore ‬در ابتدا و انتها‪ :‬برای استفاده از توابع مشخص و تعریرف‬
‫شده در پایتون استفاده مغشود‪ .‬از این حالت برای نام گذاری صفت هرای یرک‬
‫پالس استفاده نانمد تا از تداخل پمشگمری شود‪.‬‬
‫‪ :Single underscore ‬گاهغ اوقات بررای اسرتفاده از نرام هرای موقرت اسرتفاده‬
‫مغشود یا متغمرهایغ په نامشان برایمان مهم نمستند‪ .‬همچنمن در سشرن ‪Python‬‬
‫‪ REPL‬برای مشاهده نتمزه محاسبه قبلغ استفاده مغشود‪.‬‬

‫‪ .5-2‬حقیقتی ناخوشایند در مورد رشتههای پایتون‬


‫ذن پایتون‪ 1‬و ایناه چگونه باید "یک روش واضح برای انزام پار باشرد" را بره یراد‬
‫آورید‪ .‬هنگامغ په بفهممد چهار روش برای انزام قالببندی متن در پایتون وجود دارد‪،‬‬
‫ممان است از تعزب سر خود را بخارانمد‪.‬‬
‫در این فصل‪ ،‬نحوه پار این چهار رویارد قالرببنردی مرتن و نقراط قروت و ضرعف‬
‫مربوط به آنها را نشران خرواهمم داد‪ .‬همچنرمن "قاعرده سرانگشرتغ سراده" بررای نحروه‬
‫انتخاب بهترین رویارد قالببندی متن را ارائه خواهمم پرد‪.‬‬
‫بمایمد یک راست برویم سر اصل مطلب‪ ،‬زیرا باید موضوعات زیرادی را بمران پنرمم‪.‬‬
‫برای ایناه مثال پوچک و سادهای بررای آزمرایی داشرته باشرمم‪ ،‬فررض مرغپنرمم پره‬
‫متغمرهای صفحهی بعد (یا در حقمقت‪ ،‬ابتها) را بررای بررسرغ قالرب بنردی رشرتههای‬
‫پایتون در نظر گرفتهایم‪.‬‬

‫‪ 1‬مزموعه ای از ‪ 20‬اصل نرمافزاری تأ مرگذار بر طراحغ زبان برنامهنویسغ پایتون‬


‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪57 ‬‬

‫‪>>> errno = 50159747054‬‬


‫'‪>>> name = 'Bob‬‬

‫و بر پایه این متغمرها‪ ،‬مایلمم رشته خروجغ را با پمام خطای زیر ایزاد پنمم‪.‬‬

‫'!‪'Hey Bob, there is a 0xbadc0ffee error‬‬

‫بروز این خطا مغتواند واقعا صبح شنبه یک توسعه دهنده را خراب پند! اما امروز در‬
‫اینزا هستمم تا درباره قالببندی متن در پایتون صحبت پنمم‪ .‬پرس بمایمرد بره پرار خرود‬
‫برسمم‪.‬‬

‫‪ .1-5-2‬قالب بندی متن به سبک قدیمی‬


‫رشتهها در پایتون دارای عملمات داخلغ منحصر بفرد هستند په مغتوان با ‪-٪‬اپراترور‬
‫به آن دست یافت‪ .‬این ممانبری است په به شما اماان قالببندی راحت متن را مغدهرد‪.‬‬
‫اگر تا حاال با تابع ‪ printf‬در زبان برنامهنویسغ ‪ C‬پار پردهاید‪ ،‬فورا نحوه پار با آن هرا‬
‫را متوجه خواهمد شد‪ .‬در زیر مثال سادهای وجود دارد‪.‬‬

‫‪>>> 'Hello, %s' % name‬‬


‫'‪'Hello, Bob‬‬

‫در اینزا از تعممنپننده قالب ‪ %s‬استفاده مغپنمم تا به پایتون بگوید په مقردار متغمرر‬
‫‪ name‬را بصورت رشته جایگزین پند‪ .‬به این قالببندی متن به "سبک قردیمغ" گفتره‬
‫مغشود‪.‬‬
‫در قالببندی متن به سبک قدیمغ‪ ،‬تعممنپنندههای دیگری از قالب نمرز وجرود دارد‬
‫پرره برره شررما اجررازه پنترررل رشررته خروجررغ را مررغدهررد‪ .‬برررای مثررال‪ ،‬تبرردیل اعررداد برره‬
‫هگزادسممال یا افزودن فضای خالغ در متن برای ایزاد جداول و گزارشهای قالببندی‬
‫شده زیبا‪ ،‬اماانپذیر است‪.‬‬
‫در قطعه پد صفحهی بعد‪ ،‬از تعممنپننده قالب ‪ %x‬برای تبدیل مقدار از عدد صحمح‬
‫به رشته و نشان دادن آن بصورت هگزادسممال استفاده مغپنمم‪.‬‬
‫‪  58‬ترفندهای پایتون‬

‫‪>>> '%x' % errno‬‬


‫'‪'badc0ffee‬‬

‫اگر بخواهمد جایگزینهای چندگانه را در رشته واحد ایزراد پنمرد‪ ،‬سرمنتاس قالرب‬
‫بندی متن به سبک قدیمغ پمغ تغممر خواهد پرد‪ .‬چون ‪-٪‬اپراتور فقط یک آرگومران‬
‫را مغگمرد‪ .‬شما باید طرف راست را در یک ‪ ،tuple‬مانند دستور زیر قرار دهمد‪.‬‬

‫)‪>>> 'Hey %s, there is a 0x%x error!' % (name, errno‬‬


‫'!‪'Hey Bob, there is a 0xbadc0ffee error‬‬

‫همچنمن مغتوانمد قطعه پد باال را به صورت زیر بنویسمد تا عملمات جایگزینغ انزام‬
‫شود‪.‬‬

‫{ ‪>>> 'Hey %(name)s, there is a 0x%(errno)x error!' %‬‬


‫} ‪"name": name, "errno": errno‬‬
‫'!‪'Hey Bob, there is a 0xbadc0ffee error‬‬

‫این پار باعث تغممر و اصالح سادهتر رشتهها در آینده مغشود و نمازی نمست نگرران‬
‫ترتمب جایگزینغ صحمح ‪-٪‬اپراتور ها باشمد‪ .‬البته نقطه ضعف این روش این است په به‬
‫پمغ تایپ بمشتری نماز دارد‪.‬‬
‫مطمئن هستم تعزب پردهاید په چرا این قالببندی به سبک ‪ printf‬قالببندی متن‬
‫به "سبک قدیمغ" ناممده مغشود‪ .‬خب‪ ،‬اجازه دهمد بره شرما بگرویم‪ .‬سربک قردیمغ از‬
‫لحاظ فنغ پس از مدتغ‪ ،‬به طور مزدد جایگزین قالببندی به "سبک جدید" شرد‪ ،‬پره‬
‫در ادامه مغخواهمم پمرغ دربراره آن صرحبت پنرمم‪ .‬برا ایناره قالرببنردی بره "سربک‬
‫قدیمغ" به طور پامل ترجمح داده نشده است‪ ،‬اما پامالد هم پنار گذاشته نشرده و هنروز‬
‫در آخرین نسخههای پایتون پشتمبانغ مغشود‪.‬‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪59 ‬‬

‫‪ .2-5-2‬قالببندی متن به سبک جدید‬


‫پایتون ‪ 3‬روشغ جدید برای انزام قالببندی متن ارائه داد په بعردا بره پرایتون ‪- 2 .7‬‬
‫انتقال داده شد‪ .‬این قالببنردی مرتن بره "سربک جدیرد" از سرمنتاس ویرژه ‪-٪‬اپراترور‬
‫مغشود و باعث منظمترشدن سمنتاس برای قالرببنردی مرتن مرغشرود‪ .‬امرروزه‬ ‫خال‬
‫قالببندی با فراخوانغ تابع )(‪ format‬در شغء رشته‪ ،‬مدیریت مغشود‪.‬‬
‫مغتوانمد از تابع )(‪ format‬برای انزام قالببندی موقعمتغ ساده استفاده پنمرد‪ ،‬دقمقرا‬
‫مانند آنچه په مغتوانستمد با قالببندی به "سبک قدیمغ" انزام دهمد‪.‬‬

‫)‪>>> 'Hello, {}'.format(name‬‬


‫'‪'Hello, Bob‬‬

‫یا مغتوانمد طبق نام به جایگزینهای متغمرها رجو پرده و از آنها به هر ترتمرب پره‬
‫بخواهمد استفاده پنمد‪ .‬این ویژگغِ پامالد قدرتمنردی اسرت‪ ،‬چرون اجرازه تنظرمم مزردد‬
‫ترتمب نمایی بدون تغممر آرگومانهای ورودی به تابع ‪ format‬را مغدهد‪.‬‬

‫}‪>>> 'Hey {name}, there is a 0x{errno:x‬‬


‫(‪error!'.format‬‬
‫)‪name=name, errno=errno‬‬
‫'!‪'Hey Bob, there is a 0xbadc0ffee error‬‬

‫این روش همچنمن نشان مغدهد په سمنتاس برای قالببندی متغمر به صورت رشرته‬
‫هگزادسممال در برنامه تغممر پرده است‪ .‬باید با افرزودن پسروند "‪ ":x‬پرس از نرام متغمرر‪،‬‬
‫مشخصات قالب را منتقل پنمم‪.‬‬
‫در حالت پلغ‪ ،‬سمنتاس جدید قالب بندی رشته ها‪ ،‬سادهتر و قدرتمندتر شده است‪.‬‬
‫در پایتون ‪ ،3‬این قالببندی متن به "سبک جدید" بر قالببندی به سبک ‪-%‬اپراترور‬
‫ترجمح داده مغشود‪ .‬با این حال‪ ،‬با انتشار پایتون ‪ 3.6‬حتغ روشغ بهتر بررای قالرببنردی‬
‫متنهای شما وجود دارد‪ .‬در بخی بعدی‪ ،‬در مورد آن همه چمز را خواهم گفت‪.‬‬
‫‪  60‬ترفندهای پایتون‬

‫‪ .3-5-2‬درج یا الحاق رشته های قالببندی شده (پایتون‪)3.6+‬‬


‫پایتون ‪ 3.6‬روشغ دیگر برای قالببندی رشتهها‪ ،‬تحت عنوان الحاق رشتههای قالرب‬
‫بندی شده ‪1‬اضافه مغپند‪ .‬این روش جدید برای قالببندی رشتهها به شما اماان استفاده‬
‫از شغهای مقداردهغ شده پایتون در داخل رشته را فراهم مغآورد‪ .‬در اینزا مثالغ ساده‬
‫برای درک این ویژگغ وجود دارد‪.‬‬

‫\ '!}‪>>> f'Hello, {name‬‬


‫'!‪'Hello, Bob‬‬

‫این سمنتاس جدید قالببندی‪ ،‬قدرتمنرد اسرت‪ .‬چرون مرغتوانمرد هرر نرو عبرارات‬
‫دلخواه پایتون را در رشتهی خود جاسازی پنمد‪ ،‬حتغ مغتوانمد محاسبات درونخطغ را‬
‫با آن انزام دهمد‪ ،‬مانند مثال زیر‪.‬‬

‫‪>>> a = 5‬‬
‫‪>>> b = 10‬‬
‫‪>>> f'Five plus ten is {a + b} and not {2 * (a +‬‬
‫'‪b)}.‬‬
‫'‪'Five plus ten is 15 and not 30.‬‬

‫در پشت صحنه‪ ،‬الحاق رشتههای قالببندی شده‪ ،‬یک ویژگغ تززیهگر پایتون است‬
‫په ‪ f-strings‬را به یک سری از عبارات رشته تبدیل مغپند‪ .‬سرپس ایرن عبرارات بررای‬
‫ایزاد رشته نهایغ به هم متصل مغشوند‪.‬‬
‫تصور پنمد تابع ‪ greet‬زیر په شامل ‪ f-strings‬است را داریم‪.‬‬

‫‪>>> def greet(name, question):‬‬


‫"?}‪return f"Hello, {name}! How's it {question‬‬

‫)'‪>>> greet('Bob', 'going‬‬


‫"?‪"Hello, Bob! How's it going‬‬

‫‪1 Formatted String Literals‬‬


‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪61 ‬‬

‫هنگامغ په تابع را تززیه مغپنمم و آنچه را پره در پشرت صرحنه اتفراق مرغافترد را‬
‫بررسغ مغپنمم‪ ،‬مشاهده مغپنمم په ‪ f-strings‬در این تابع به چمرزی شربمه بره پرد زیرر‬
‫تبدیل مغشود‪.‬‬

‫‪>>> def greet(name, question):‬‬


‫" ‪return ("Hello, " + name + "! How's it‬‬
‫)"?" ‪+question +‬‬

‫پمادهسازی واقعغ پمغ سریعتر از آن است چرون از آپارد ‪ 1BUILD_STRING‬بررای‬


‫بهمنهسازی استفاده مغپند‪ .‬اما به لحاظ پارپردی یاسان هستند‪.‬‬

‫‪>>> import dis‬‬


‫)‪>>> dis.dis(greet‬‬
‫‪2‬‬ ‫‪0 LOAD_CONST‬‬ ‫‪1‬‬ ‫)' ‪('Hello,‬‬
‫‪2 LOAD_FAST‬‬ ‫‪0‬‬ ‫)‪(name‬‬
‫‪4 FORMAT_VALUE‬‬ ‫‪0‬‬
‫‪6 LOAD_CONST‬‬ ‫‪2‬‬ ‫)" ‪("! How's it‬‬
‫‪8 LOAD_FAST‬‬ ‫‪1‬‬ ‫)‪(question‬‬
‫‪10 FORMAT_VALUE‬‬ ‫‪0‬‬
‫‪12 LOAD_CONST‬‬ ‫‪3‬‬ ‫)'?'(‬
‫‪14 BUILD_STRING‬‬ ‫‪5‬‬
‫‪16 RETURN_VALUE‬‬

‫همچنمن الحاق رشتههای قالببندی شده از سمنتاس موجود در روش )(‪str.format‬‬


‫پشتمبانغ مغپنند‪ .‬این پار برای شما اماران حرل مسرائل قالرببنردی رشرتهها‪ ،‬مشرابه دو‬
‫بخی قبلغ را فراهم مغپند‪.‬‬

‫\ "!‪>>> f"Hey {name}, there's a {errno:#x} error‬‬


‫"!‪"Hey Bob, there's a 0xbadc0ffee error‬‬

‫رشتههای نوشتاری جدید قالببندی شده در پایتون شبمه به الگوهای نوشرتاری جراوا‬
‫اساریپت در ‪ ES2015‬است‪ .‬فار مغپنم ویژگغ پامالد خوبغ برای زبان هسرتند و قربالد‬

‫‪1 Opcode‬‬
‫‪  62‬ترفندهای پایتون‬

‫از آنها در پارهای روزانه پایتون ‪ 3‬اسرتفاده پرردهام‪ .‬در مسرتندات رسرمغ پرایتون مرغ‬
‫توانمد مطالب بمشتری در مورد الحاق رشتههای قالب بندی شده یاد بگمرید‪.‬‬

‫‪ .4-5-2‬الگوهای رشتهها‬
‫یک روش دیگر برای قالببندی رشته در پایتون‪ ،‬استفاده از الگوی رشتهها (یا قالب‬
‫رشتهها) ‪1‬است‪ .‬این ماانمزمغ سادهتر و با صرف هزینه پمتر است‪ ،‬اما در برخرغ مروارد‬
‫ممان است دقمقا همان چمزی باشد په به دنبال آن هستمد‪.‬‬
‫اجازه دهمد تا یک مثال ساده را بررسغ پنمم‪.‬‬

‫‪>>> from string import Template‬‬


‫)'!‪>>> t = Template('Hey, $name‬‬
‫)‪>>> t.substitute(name=name‬‬
‫'!‪'Hey, Bob‬‬

‫در اینزا مغبمنمد په نماز به ‪ import‬پرردن پرالس ‪ Template‬را از مراژول داخلرغ‬


‫رشته پایتون داریم‪ .‬الگوی رشتهها‪ ،‬ویژگغ اصلغ زبان نمستند بلاه با ماژولغ در پتابخانه‬
‫استاندارد پایتون تعبمه شدهاند‪.‬‬
‫تفاوت دیگر این است په الگوی رشتهها اجازه مشخص پرردن فرمرت اشرما را نمرغ‬
‫دهند‪ .‬بنابراین به منظور نمایی نمونه رشته ی خطا‪ ،‬باید عدد خطا از جنس عدد صرحمح‬
‫را به رشته هگزادسممال تبدیل پنمم‪.‬‬

‫‪>>> templ_string = 'Hey $name, there is a $error‬‬


‫'!‪error‬‬
‫(‪>>> Template(templ_string).substitute‬‬
‫))‪name=name, error=hex(errno‬‬
‫'!‪'Hey Bob, there is a 0xbadc0ffee error‬‬

‫نحوه پار این روش عالغ است‪ ،‬به عقمده مرن‪ ،‬بهتررین مرورد اسرتفاده بررای الگروی‬
‫رشتهها زمانغ است په رشرتههرای ورودی تولمرد شرده توسرط پراربران را قالرب بنردی‬

‫‪1 Template Strings‬‬


‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪63 ‬‬

‫مغپنمد‪ .‬زیرا به دلمل پاهی مخراطرات ورودی هرای ارسرالغ از طررف پراربران‪ ،‬ایرن‬
‫روش انتخاب امن تری است‪.‬‬
‫قالببندی بسمار پمچمده رشتهها ممان است آسرمبپرذیریهرای امنمترغ را در برنامره‬
‫شما ایزاد پند‪ .‬برای مثال‪ ،‬این اماان برای قالب بندی نادرست رشتهها وجرود دارد پره‬
‫به متغمرهای دلخواه در برنامه شما دسترسغ پمدا پنند‪.‬‬
‫این بدین معناست په اگر پاربر مخرب بتواند رشته قالب را تهمه پند‪ ،‬پرس احتمراالد‬
‫مغتواند به پلمدهای مخفغ و سایر اطالعات حساس نمز نفوذ پند! در اینزا ا برات سراده‬
‫ای درباره نحوه استفاده از این حمله وجود دارد‪.‬‬

‫'‪>>> SECRET = 'this-is-a-secret‬‬


‫‪>>> class Error:‬‬
‫‪def __init__(self):‬‬
‫‪pass‬‬

‫)(‪>>> err = Error‬‬


‫]‪>>> user_input = '{error.__init__.__globals__[SECRET‬‬
‫'}‬

‫‪# Uh-oh...‬‬
‫)‪>>> user_input.format(error=err‬‬
‫'‪'this-is-a-secret‬‬

‫ببمنمد چگونه مهاجم فرضغ توانست با دسترسغ به دیاشنری __‪ __globals‬از قالرب‬
‫رشته‪ ،‬رشته مخفغ ما را استخراج پند؟ ترسناک است! الگوی رشتهها‪ ،‬این نو حمله را‬
‫خنثغ مغپنند و در صورت دستااری قالرب رشرتههرای تولمرد شرده از ورودی پراربر‪،‬‬
‫پاس ایمنتری مغدهند‪.‬‬

‫= ‪>>> user_input‬‬
‫'}]‪'${error.__init__.__globals__[SECRET‬‬
‫)‪>>> Template(user_input).substitute(error=err‬‬
‫‪ValueError:‬‬
‫"‪"Invalid placeholder in string: line 1, col 1‬‬
‫‪  64‬ترفندهای پایتون‬

‫‪ .5-5-2‬از کدام روش قالببندی رشتهها باید استفاده کنیم؟‬


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

‫‪ .6-5-2‬قاعده سرانگشتی قالببندی متن در پایتون‬


‫اگر رشتههای ورودی برنامه شما توسط پاربر ارسال شده‪ ،‬از الگوی رشرتههرا بررای‬
‫جلرروگمری از مشرراالت امنمتررغ اسررتفاده پنمررد‪ .‬در رمراینصررورت‪ ،‬اگررر از پررایتون ‪3 .6+‬‬
‫استفاده مغپنمد‪ ،‬از الحاق یا درج رشته قالببندی شده اسرتفاده پنمرد و اگرر از پرایتون‬
‫‪ 3 .6+‬استفاده نمغپنمد از قالببندی متن به "سبک جدید" استفاده پنمد‪.‬‬

‫‪ .7-5-2‬نکات کلیدی در هنگام کار با رشتهها در پایتون‬


‫‪ ‬شاید تعزبآور باشد‪ ،‬در پایتون بمی از یک روش بررای مردیریت قالرببنردی‬
‫متن وجود دارد‪.‬‬
‫‪ ‬هر روش دارای جوانب مثبت و منفغ جداگانهای اسرت‪ .‬مرورد اسرتفاده شرما برر‬
‫انتخاب روش‪ ،‬ا ر خواهد گذاشت‪.‬‬
‫‪ ‬اگر در رابطه با ایناره از پردام روش قالرببنردی رشرته اسرتفاده پنمرد‪ ،‬مشرال‬
‫تصممم گمری دارید‪ ،‬قاعده سرانگشتغ قالببندی رشته من را امتحان پنمد‪.‬‬
65  ‫ کدنویسی تمیز با پایتون‬:‫فصل دوم‬

‫ ذن پایتون‬.6-2
‫ یک دید مشترک‬،‫ تا جایغ په پایتون ادامه دارد‬،‫مغدانمم آنچه په به شرح زیر است‬
‫ اما واقعا همک روشرغ در مرورد اجررای ذن پرایتون وجرود‬.‫بمن برنامهنویسان پایتون است‬
‫ و فارر مرغپرنم سرخنان ترمم‬،‫ من از بازبمنغ ذن پایتون در این سال ها سود بردهام‬.‫ندارد‬
‫ اممدوارم ذن پایتون بتواند هممن پار را برای‬.‫مرا به برنامهنویس بهتری تبدیل پرد‬1 ‫پمترز‬
.‫شما انزام دهد‬
‫برای مشاهده ذن پایتون الزم است وارد مفسر پایتون شوید و چنمن دستوری را اجررا‬
.‫پنمد‬

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.


Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren’t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one—and preferably only one—obvious way to do it.
Although that way may not be obvious at first unless you’re Dutch. Now is better than
never.
Although never is often better than right now.
If the implementation is hard to explain, it’s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea—let’s do more of those!

1 Tim Peters
‫‪  66‬ترفندهای پایتون‬

‫ذن پایتون‪ ،‬نوشته شده توسط تیم پیترز‬


‫زیبا بهتر از زشت است‪.‬‬
‫بمان صریح بهتر از ضمنغ است‪.‬‬
‫ساده بهتر از پمچمده است‪.‬‬
‫پمچمده بهتر از خملغ پمچمده (افتضاح) است‪.‬‬
‫مستقمم و صاف بهتر از تو در تو است‪.‬‬
‫پراپنده بهتر از متراپم است‪.‬‬
‫خوانایغ مهم است‪.‬‬
‫موارد ویژه به اندازهای ویژه نمستند په به خاطر آنها بتوان قوانمن را شاست‪.‬‬
‫را از بمن مغبرد‪.‬‬ ‫گرچه عملغ بودن خلو‬
‫خطاها هرگز نباید با ساوت رد شوند‪.‬‬
‫مگر ایناه صراحتا خاموش شوند‪.‬‬
‫در مواجهه با ابهام‪ ،‬از وسوسهی حدس زدن دوری پنمد‪.‬‬
‫برای انزام یک پار باید یک (ترجمحا یک) روش واضح وجود داشته باشد‪.‬‬
‫هر چند ممان است این روش در ابتدا واضح و آشاار نباشد مگر ایناه شما هلندی‬
‫باشمد‪.‬‬
‫حاال‪ ،‬بهتر از هرگز است‪.‬‬
‫اگر چه هرگز‪ ،‬ارلب راحت تر از حال حاضر است‪.‬‬
‫اگر توضمح پمادهسازی دشوار باشد‪ ،‬پس ایده بدی خواهد بود‪.‬‬
‫اگر توضمح پمادهسازی آسان باشد‪ ،‬پس ایده خوبغ خواهد بود‪.‬‬
‫فضاهای نام یک ایده عالغ است‪ ،‬بمایمد بمشتر از این پارها پنمم!‬
‫فصل دوم‪ :‬کدنویسی تمیز با پایتون ‪67 ‬‬

‫به این جمرالت ذن ‪ 20‬گانره پرایتون مغگوینرد‪ .‬ذن پرایتون مزموعرهای از ‪ 20‬اصرل‬
‫بنمادین هنگام طراحغ زبان پایتون است په توسط تمم پمتررز در سرال ‪ 1999‬نوشرته شرده‬
‫است‪ .‬ناته ی جالب در رابطه با ذن پایتون این است پره ترمم پمتررز تنهرا ‪ 19‬اصرل از ‪20‬‬
‫اصل را بمان پرده اسرت و اصرل شرماره ‪ 20‬را بررای خرالق زبران پرایتون‪Guido Van ،‬‬
‫‪ Rossum‬پنار گذاشته است‪ .‬اصل ‪ 20‬ذن پایتون تا لحظهی نگرارش ایرن پتراب توسرط‬
‫خالق زبان پایتون بمان نشده است‪.‬‬
‫فصل سوم‪ :‬توابع تأ مرگذار‬

‫فصل سوم‬

‫توابع تأثیرگذار‬
‫‪ .1-3‬توابع پایتون شهروندان درجه اول‪ 1‬هستند‬
‫دهمرد‪،‬‬ ‫توابع پایتون‪ ،‬اشماء درجه اول هستند‪ .‬مغتوانمد آنها را به متغمرهرا اختصرا‬
‫آنها را در ساختار دادهها ذخمره پنمد‪ ،‬آنها را به عنروان آرگومانهرایغ بره سرایر توابرع‬
‫منتقل پنمد و حتغ آنها را بعنوان مقداری از سایر توابع برگردانمد‪.‬‬
‫درک شهودی این مفاهمم باعث درک بسمار راحت ویژگغ های پمشررفته در پرایتون‬
‫مانند المبداها‪ 2‬و دپوراتورها‪ 3‬خواهد شد‪ .‬همچنمن شرما را بره سرمت روشهرای برنامره‬
‫نویسغ تابعغ‪ 4‬سوق مغدهد‪.‬‬
‫در چند صفحه بعد‪ ،‬شما را از طریق برخغ مثالها راهنمایغ خواهمم پرد تا به شما در‬
‫توسعه این درک شهودی پمک پنمم‪ .‬این مثالها روی یادیگر قرار خواهنرد گرفرت‪،‬‬
‫بنابراین ممان است بخواهمد آنها را به ترتمب بخوانمد و در صورت تداوم ممارن اسرت‬
‫حتغ برخغ از آنها را در مفسر پایتون امتحان پنمد‪.‬‬
‫به مفاهممغ په در اینزا مورد بحث قرار خواهمم داد‪ ،‬پمغ فار پنمرد‪ .‬ممارن اسرت‬
‫پمغ بمشتر از آنچه انتظار دارید‪ ،‬طول باشد‪ .‬نگران نباشرمد‪ ،‬ایرن شررایط پرامالد طبمعرغ‬
‫است‪ .‬من در این شرایط بودهام‪ .‬ممان است حس پنمد په بخواهمد سرتان را بره دیروار‬
‫باوبممد و بعد هنگامغ په آماده هستمد‪ ،‬ناگهران همره چمرز "پلمرک" خواهرد خرورد و‬
‫مفاهمم در ذهنتان در جای درست قرار خواهند گرفت‪.‬‬
‫در سرتاسر این فصل‪ ،‬از تابع ‪ yell‬برای اهداف آموزشغ استفاده خرواهمم پررد‪ .‬ایرن‬
‫مثال سادهای است په خروجغ آن را به راحتغ مغتوان تشخمص داد‪.‬‬

‫‪1 First-class‬‬
‫‪2 lambdas‬‬
‫‪3 decorators‬‬
‫‪4 Functional programming‬‬
‫‪  72‬ترفندهای پایتون‬

‫‪>>> def yell(text):‬‬


‫'!' ‪return text.upper() +‬‬

‫)'‪>>> yell('hello‬‬
‫'!‪'HELLO‬‬

‫‪ .1-1-3‬توابع اشیا هستند‬


‫تمام دادههای موجود در برنامه پایتون با اشماء یا روابط بمن اشماء نشان داده مغشوند‪.‬‬
‫چمزهایغ مانند رشتهها‪ ،‬لمستها‪ ،‬ماژولها و توابع همگغ اشماء هستند‪ .‬همک چمرز خاصرغ‬
‫در مورد توابع پایتون وجود ندارد‪ .‬آنها نمز فقط اشماء هستند‪.‬‬
‫چون تابع ‪ yell‬یک شغء در پایتون اسرت‪ ،‬پرس مرغتوانمرد درسرت ماننرد هرر شرغء‬
‫دهمد‪.‬‬ ‫دیگری آن را به متغمر دیگری اختصا‬

‫‪>>> bark = yell‬‬

‫این خط تابع را فراخوانغ نمغپند‪ .‬این خط‪ ،‬شغء تابعغ به نام ‪ yell‬را مغگمرد و آن‬
‫را در شغء ‪ bark‬په به آن اشاره مغپند‪ ،‬قرار مغدهد‪ .‬حال مغتوانمد با فراخوانغ ‪،bark‬‬
‫همان تابع اصلغ ‪ yell‬را نمز اجرا پنمد‪.‬‬

‫)'‪>>> bark('woof‬‬
‫'!‪'WOOF‬‬

‫اشمای تابع و نامهای آنها دو مسأله جداگانه هستند‪ .‬پمغ عممقتر بنگریم‪ ،‬شرما مرغ‬
‫توانمد نام اصلغ تابع (‪ )yell‬را حذف پنمد‪ .‬چون نام دیگرر (‪ )bark‬هنروز بره ترابع اصرلغ‬
‫اشاره مغپند‪ ،‬سپس مغتوانمد این تابع را از طریق دستور زیر فراخوانغ پنمد‪.‬‬

‫‪>>> del yell‬‬

‫)'?‪>>> yell('hello‬‬
‫"‪NameError: "name 'yell' is not defined‬‬

‫)'‪>>> bark('hey‬‬
‫'!‪'HEY‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪73 ‬‬

‫به هر حال‪ ،‬پایتون با اهداف اشاالزدایغ و عمبیابغ‪ ،‬شناسه نام ترابع را بره ترابع در‬
‫زمان اجرا‪ ،‬متصل مغپند‪ .‬با تابع جادویغ __‪ __name‬مرغتوانمرد بره ایرن شناسرهی نرام‬
‫داخلغ دسترسغ پمدا پنمد‪.‬‬

‫__‪>>> bark.__name‬‬
‫'‪'yell‬‬

‫حال‪ ،‬با ایناه مقدار __‪ __name‬تابع هنوز "‪ "yell‬است‪ ،‬اما بر نحوه دسترسرغ شرما‬
‫به شغء تابع ا ر نمغگذارد‪ .‬شناسه نام تابع صررفاد بررای پمرک بره اشراالزدایرغ اسرت‪.‬‬
‫بنابراین در این قسمت متوجه شدیم متغمری په به نام تابع اشاره مغپند و خود ترابع‪ ،‬دو‬
‫نگرانغ متفاوت هستند‪.‬‬

‫‪ .2-1-3‬توابع میتوانند در ساختارهای داده ذخیره شوند‬


‫چون توابع شهروندان درجه اول هستند‪ ،‬مغتوانمد آنها را در ساختارهای داده ذخمره‬
‫پنمد‪ ،‬درست مانند پارهایغ په مغتوانمد با سایر اشماء انزام دهمد‪ .‬برای مثال‪ ،‬مغتوانمرد‬
‫توابع را به لمست اضافه پنمد‪.‬‬

‫]‪>>> funcs = [bark, str.lower, str.capitalize‬‬


‫‪>>> funcs‬‬
‫‪[<function yell at 0x10ff96510>,‬‬
‫‪<method 'lower' of 'str' objects>,‬‬
‫]>‪<method 'capitalize' of 'str' objects‬‬

‫دسترسغ به اشمای تابع ذخمره شده در لمست‪ ،‬مانند دسترسغ به هر نو شغء دیگرری‬
‫در زبان پایتون است‪.‬‬

‫‪>>> for f in funcs:‬‬


‫))'‪print(f, f('hey there‬‬
‫'!‪<function yell at 0x10ff96510> 'HEY THERE‬‬
‫'‪<method 'lower' of 'str' objects> 'hey there‬‬
‫'‪<method 'capitalize' of 'str' objects> 'Hey there‬‬
‫‪  74‬ترفندهای پایتون‬

‫حتغ مغتوانمد شغء تابع ذخمره شده در لمست را به صورت مستقمم فراخوانغ پنمد‪.‬‬

‫)'‪>>> funcs[0]('heyho‬‬
‫'!‪'HEYHO‬‬

‫‪ .3-1-3‬توابع را میتوان به توابع دیگر پاس داد‬


‫چون توابع بصورت اشماء هستند‪ ،‬پس مغتوانمرد آنهرا را بره عنروان آرگومران هرای‬
‫ورودی به توابع دیگر پاس دهمد‪ .‬در اینزا تابع ‪ greet‬را نوشتهایم په به عنوان آرگومان‬
‫ورودی یک تابع را دریافت مغپند و سپس یک رشته را به عنوان ورودی بره ایرن ترابع‬
‫مغدهد‪ .‬در نهایت نتمزه را نمایی مغدهد‪.‬‬

‫‪def greet(func):‬‬
‫)'‪greeting = func('Hi, I am a Python program‬‬
‫)‪print(greeting‬‬

‫مغتوانمد با انتقال تابع به توابع مختلف‪ ،‬بر نتمزه ا ر بگذارید‪ .‬برای مثال در قطعه پرد‬
‫زیر تابع ‪ bark‬را به عنوان ورودی تابع ‪ greet‬دادیم ترا در نتمزره متغمرر ‪ greeting‬ترا مر‬
‫بگذاریم‪.‬‬

‫)‪>>> greet(bark‬‬
‫'!‪'HI, I AM A PYTHON PROGRAM‬‬

‫البته‪ ،‬مغتوانمد تابع جدیدی را نمز برای ایزاد ویژگغهای مختلف تعریف پنمد‪ .‬برای‬
‫مثال‪ ،‬اگر نمغخواهمرد برنامره پرایتون شرما ماننرد اپتممروس پررایم ‪1‬بره نظرر برسرد‪ ،‬ترابع‬
‫‪ whisper‬زیر ممان است عملارد بهتری داشته باشد‪( .‬زیرا بره جرای حرروف برزرگ‪،‬‬
‫حروف پوچک یک متن را نمایی مغدهد)‬

‫‪>>> def whisper(text):‬‬


‫'‪return text.lower() + '...‬‬

‫)‪>>> greet(whisper‬‬
‫'‪'hi, i am a python program...‬‬

‫یک شخصیت داستانی – ‪1 Optimus Prime‬‬


‫فصل سوم‪ :‬توابع تأثیرگذار ‪75 ‬‬

‫توانایغ انتقال اشماء تابع به صورت آرگومانهایغ به توابع دیگرر‪ ،‬ویژگرغ قدرتمنردی‬
‫است‪ .‬این پار به شما اجازه مغدهد تا انتزا را پنار بگذارید و بر رفترار در برنامرههرای‬
‫خود تمرپز پنمد‪ .‬در این مثرال‪ ،‬ترابع ‪ greet‬یاسران مرغمانرد‪ ،‬امرا مرغتوانمرد برا انتقرال‬
‫رفتارهای مختلف‪ ،‬بر خروجغ آن ا ر بگذارید‪.‬‬
‫توابعغ په مغتوانند سایر توابع را بعنوان آرگومانها بپذیرند‪ ،‬توابع مرتبه براالتر‪ 1‬نمرز‬
‫ناممده مغشوند‪ .‬آنها الزامغ برای سبک برنامهنویسغ تابعغ‪ 2‬هستند‪.‬‬
‫مثال پالسمک برای توابع مرتبه باالتر در پایتون‪ ،‬ترابع داخلرغ ‪ map‬در زبران پرایتون‬
‫است‪ .‬این شغء تابع و تاررار شرونده ‪3‬را مرغگمررد و بعرد ترابع را روی هرر عنصرری در‬
‫تارارشونده فراخوانغ مغپند و همانطور په پمی مغرود‪ ،‬نتایج را بدست مغ آورد‪.‬‬
‫در اینزا نحوه قالببندی توالغ احوالپرسغ را بصورت یازا با نگاشتغ از ترابع ‪bark‬‬
‫نوشته ایم په نتمزه در نهایت در یک لمست ذخمره مغشود‪.‬‬

‫))]'‪>>> list(map(bark, ['hello', 'hey', 'hi‬‬


‫]'!‪['HELLO!', 'HEY!', 'HI‬‬

‫همانطور په دیدید نگاشت در پل لمست انزام شده و تابع ‪ bark‬به هر عنصر لمسرت‬
‫اعمال شد‪ .‬در نتمزه‪ ،‬حاال یک شغ لمست جدید با رشتههای اصالح شده احوالپرسرغ را‬
‫داریم‪.‬‬

‫‪ .4-1-3‬توابع را میتوان بصورت تو در تو قرار داد‬


‫شاید تعزبآور باشد‪ ،‬پایتون به توابع این اجازه را مغدهد تا در داخرل توابرع دیگرر‬
‫تعریف شوند‪ .‬اینها ارلب توابع تو در تو‪ 4‬یا توابرع داخلرغ‪ 5‬ناممرده مرغشروند‪ .‬در اینزرا‬
‫مثالغ مغزنمم‪.‬‬

‫‪1 Higher-order functions‬‬


‫‪2 Functional programming style‬‬
‫‪3 Iterable‬‬
‫‪4 Nested functions‬‬
‫‪5 Inner functions‬‬
‫‪  76‬ترفندهای پایتون‬

‫‪>>> def speak(text):‬‬


‫‪def whisper(t):‬‬
‫'‪return t.lower() + '...‬‬
‫)‪return whisper(text‬‬

‫)'‪>>> speak('Hello, World‬‬


‫'‪'hello, world...‬‬

‫هر بار په تابع ‪ speak‬را فراخوانغ مغپنمد‪ ،‬پایتون ترابع داخلرغ جدیرد ‪ whisper‬را‬
‫تعریف مغپند و بالفاصله پس از آن فرا مغخواند‪ .‬مغز من درست پمغ از اینزا به بعرد‬
‫شرو به خارش مغپند اما‪ ،‬در پل‪ ،‬این هنوز موضو نسبتا ساده ای است‪.‬‬
‫پمغ با دقت بمشتری به موضو نگاه پنمم‪ ،‬اگر بخواهمم تابع ‪ whisper‬را فراخروانغ‬
‫پنمم چه اتفاقغ خواهد افتاد؟‬

‫)'‪>>> whisper('Yo‬‬
‫‪NameError:‬‬
‫"‪"name 'whisper' is not defined‬‬

‫‪>>> speak.whisper‬‬
‫‪AttributeError:‬‬
‫"'‪"'function' object has no attribute 'whisper‬‬

‫اما اگر واقعا بخواهمد به تابع تو در توی ‪ whisper‬در خارج از تابع ‪ speak‬دسترسرغ‬
‫پمدا پنمد‪ ،‬چه اتفاقغ مغافتد؟ خب‪ ،‬توابع اشماء هستند‪ ،‬پس مغتوانمد ترابع داخلرغ را بره‬
‫فراخواننده تابع والد برگردانمد‪.‬‬
‫برای مثال‪ ،‬در ادامه ترابعغ داریرم پره دو ترابع داخلرغ را تعریرف مرغپنرد‪ .‬بسرته بره‬
‫آرگومان منتقل شده به تابع سطح باال‪ ،‬یاغ از توابع داخلغ را انتخاب مغپند و آن را به‬
‫فراخواننده برمغگرداند‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪77 ‬‬

‫‪def get_speak_func(volume):‬‬
‫‪def whisper(text):‬‬
‫'‪return text.lower() + '...‬‬
‫‪def yell(text):‬‬
‫'!' ‪return text.upper() +‬‬
‫‪if volume > 0.5:‬‬
‫‪return yell‬‬
‫‪else:‬‬
‫‪return whisper‬‬

‫توجه داشته باشمد به ایناه چگونه تابع ‪ get_speak_func‬در واقع همک یک از توابع‬
‫داخلغ خود را فراخوانغ نمغپند‪ .‬فقط تابع داخلغ مناسب را بر پایه آرگومران ‪volume‬‬
‫انتخاب مغپند و بعد شغء تابع را برمغگرداند‪.‬‬

‫)‪>>> get_speak_func(0.3‬‬
‫‪<function get_speak_func.<locals>.whisper at‬‬
‫>‪0x10ae18‬‬

‫)‪>>> get_speak_func(0.7‬‬
‫>‪<function get_speak_func.<locals>.yell at 0x1008c8‬‬

‫البته‪ ،‬مغتوانمد ادامه دهمد و با استفاده از تابع برگشت یافته‪ ،‬مسرتقمما یرا برا تخصرمص‬
‫آن به متغمر‪ ،‬فراخوانغ را انزام دهمد‪.‬‬

‫)‪>>> speak_func = get_speak_func(0.7‬‬


‫)'‪>>> speak_func('Hello‬‬
‫'!‪'HELLO‬‬

‫اجازه دهمد چند لحظه در این مورد تأمل پنمم‪ .‬این بدان معناست پره توابرع نره تنهرا‬
‫مغتوانند رفتارها را از طریق ورودیها بپذیرند‪ ،‬بلاه مغتوانند رفتارها را نمز برگرداننرد‪.‬‬
‫به نظرتان چطور است؟‬
‫همانطور په مغدانمد‪ ،‬گاهغ درک بعضغ از مفاهمم برنامهنویسغ په تودرترو هسرتند‬
‫پمغ دشوار است‪ .‬قصد دارم قبل از ادامه نوشتن پتاب‪ ،‬استراحت پوتاهغ پنم و دمرغ‬
‫به یک فنزان قهوه بزنم‪ ( .‬پمشنهاد مغپنم شما هم این پار را انزام دهمد)‬
‫‪  78‬ترفندهای پایتون‬

‫‪ .5-1-3‬توابع میتوانند حالتهای محلی را ثبت کنند‬


‫در قسمتهای قبل دیدید په چگونه توابع مغتوانند توابع داخلغ داشته باشند‪ ،‬توابرع‬
‫دیگری را به عنوان ورودی بگمرند یا به عنوان خروجغ برگردانند‪.‬‬
‫حال پمربند ایمنغ خود را محام ببندیرد چرون مرغخرواهمم پمرغ دیوانرهبرازی در‬
‫آوریم‪ .‬قصد داریم به محدوده برنامهنویسغ تابعغ عممقتر وارد شویم‪( .‬استراحت و قهوه‬
‫په یادتان نرفت؟)‬
‫توابع نه تنها مغتوانند سایر توابع را پاس پاری پنند‪ ،‬بلاه توابع داخلغ مرغتواننرد از‬
‫برخغ حاالت تابع والد باخبر باشند و مقادیری را حمل پننرد‪ .‬خرب‪ ،‬ایرن بره چره معنرغ‬
‫است؟‬
‫برای توضمح این مثال‪ ،‬مغخواهم مثرال قبلرغ ‪ get_speak_func‬را بازنویسرغ پرنم‪.‬‬
‫نسخه جدیرد ایرن ترابع‪ ،‬آرگومانهرای ‪ volume‬و ‪ text‬را مغگمررد و ترابع برگشرتغ را‬
‫بالفاصله فراخوانغ مغپند‪.‬‬

‫‪def get_speak_func(text, volume):‬‬


‫‪def whisper():‬‬
‫'‪return text.lower() + '...‬‬
‫‪def yell():‬‬
‫'!' ‪return text.upper() +‬‬
‫‪if volume > 0.5:‬‬
‫‪return yell‬‬
‫‪else:‬‬
‫‪return whisper‬‬

‫)()‪>>> get_speak_func('Hello, World', 0.7‬‬


‫'!‪'HELLO, WORLD‬‬

‫حال به توابع داخلغ ‪ whisper‬و ‪ yell‬خوب نگاه پنمرد‪ .‬دقرت پردهایرد پره چگونره‬
‫آنها دیگر پارامتر ‪ text‬را به عنوان ورودی ندارند؟ اما هنروز مرغتواننرد بره پرارامتر ‪text‬‬
‫تعریف شده در تابع والد دسترسغ پمدا پنند‪ .‬در حقمقت‪ ،‬اینطور به نظر مغرسد په آنهرا‬
‫مقدار آرگومانهای والد را بت پرده و به یاد مغآورند‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪79 ‬‬

‫توابعغ په این پار را انزام مغدهند‪ ،‬بستارهای معنرایغ ‪1‬ناممرده مرغشروند (یرا بطرور‬
‫خالصه‪ ،‬فقط بستارهرا ‪2‬ناممرده مرغشروند)‪ .‬بسرتار‪ ،‬مقرادیر را از ترابع والرد خرود بره یراد‬
‫مغآورد‪ ،‬حتغ اگر جریان اجرای برنامه دیگر در این تابع نباشد‪ .‬به زبان ساده‪ ،‬این بردان‬
‫معناست په توابع نه تنها مغتوانند رفتارها را برگردانند بلاه مغتوانند از قبل‪ ،‬آن رفتارها‬
‫را پماربندی پنند‪ .‬برای توضمح این ایده‪ ،‬مثالغ مغزنم‪.‬‬

‫‪>>> def make_adder(n):‬‬


‫‪def add(x):‬‬
‫‪return x + n‬‬
‫‪return add‬‬

‫)‪>>> plus_3 = make_adder(3‬‬


‫)‪>>> plus_5 = make_adder(5‬‬

‫)‪>>> plus_3(4‬‬
‫‪7‬‬
‫)‪>>> plus_5(4‬‬
‫‪9‬‬

‫در این مثال‪ make_adder ،‬به عنوان دیزاین پترن ‪3 factory‬برای ایزاد و پماربندی‬
‫تابع ‪ add‬عمل مغپند‪ .‬دقت پنمرد پره چگونره ترابع ‪ add‬مغتوانرد بره مقردار ‪ n‬از قبرل‬
‫مقداردهغ شده‪ ،‬دسترسغ پمدا پند‪ .‬بهترر اسرت زمرانغ را بررای درک عممرق ایرن مثرال‬
‫دهمد‪.‬‬ ‫اختصا‬

‫‪ .6-1-3‬اشیاء میتوانند مانند توابع رفتار کنند‬


‫با ایناه همه توابع در پایتون اشماء هستند‪ ،‬اما عاس آن درسرت نمسرت‪ .‬اشرماء توابرع‬
‫نمستند‪ .‬اما مغتوان آنها را فراخوانغ پرد په به شما اجازه مغدهد در ارلب موارد با آن‬
‫ها مانند توابع رفتار پنمد‪.‬‬

‫‪1 Lexical closures‬‬


‫‪2 Closures‬‬
‫‪ Creational Design Patterns 3‬در دسته ‪ Factory‬مراجعه شود به دیزاین پترن‬
‫‪  80‬ترفندهای پایتون‬

‫اگر شغ قابل فراخوانغ باشد به این معنغ است په مغتوانمد از سمنتاس پرانترز براز و‬
‫بسته برای آن استفاده پنمد و در صورت نماز مقادیری را به شغ پاس دهمد‪ .‬این قابلمت با‬
‫استفاده از متد جادویغ __‪ __call‬انزام مغشود‪.‬‬

‫‪class Adder:‬‬
‫‪def __init__(self, n):‬‬
‫‪self.n = n‬‬
‫‪def __call__(self, x):‬‬
‫‪return self.n + x‬‬

‫)‪>>> plus_3 = Adder(3‬‬


‫)‪>>> plus_3(4‬‬
‫‪7‬‬

‫در پشت صحنه‪ ،‬زمانغ په یک شغ را همانند تابع فراخوانغ مغپنمد‪ ،‬پرایتون ترالش‬
‫مغپند تا تابع __‪ __call‬درون شغ را فراخوانغ پند‪.‬‬
‫البته همه اشماء قابل فراخوانغ نمستند‪ .‬به هممن دلمل یک تابع ‪ callable‬داخلغ وجرود‬
‫دارد تا بررسغ پند په آیا شغء قابل فراخوانغ است یا خمر‪.‬‬

‫)‪>>> callable(plus_3‬‬
‫‪True‬‬
‫)‪>>> callable(yell‬‬
‫‪True‬‬
‫)'‪>>> callable('hello‬‬
‫‪False‬‬

‫‪ .7-1-3‬نکات کلیدی توابع به عنوان شهروندان درجه اول‬


‫‪ ‬همه چمز‪ ،‬از جمله توابع در پرایتون‪ ،‬شرغ اسرت‪ .‬مرغتوانمرد آنهرا را بره متغمرهرا‬
‫دهمد‪ ،‬آنها را در ساختارهای داده ذخمره پنمد و به سایر توابع منتقرل‬ ‫اختصا‬
‫پنمد یا از سایر توابع برگردانمد‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪81 ‬‬

‫‪ ‬توابع‪ ،‬به عنوان شهروندان درجه اول به شما اجرازه مرغدهنرد ترا انترزا را پنرار‬
‫بگذارید و بر رفتارهای برنامه خود توجه پنمد‪.‬‬
‫‪ ‬توابع را مغتوان به صورت تو در تو ایزاد پرد و آنها مغتوانند برخرغ حراالت‬
‫تابع والد را بت پرده و حمل پننرد‪ .‬تروابعغ پره ایرن پرار را انزرام مرغدهنرد‪،‬‬
‫بستارها ناممده مغشوند‪.‬‬
‫‪ ‬اشماء را نمز مغتوان قابل فراخوانغ پرد‪ .‬در ارلب موارد‪ ،‬این پار به شرما اجرازه‬
‫مغدهد تا با آنها مانند توابع رفتار پنمد‪.‬‬

‫‪ .2-3‬المبداها‪ ،‬توابع تک جملهای‬


‫پلمدواژه ‪ lambda‬در پایتون‪ ،‬ممانبری برای تعریرف توابرع ناشرناس پوچرک اسرت‪.‬‬
‫توابع المبدا درست مانند توابع عادی تعریف شده با پلمدواژه ‪ def‬رفتار مغپنند‪.‬‬
‫برای مثال‪ ،‬اینگونه تابع ساده المبدا را په عملمات جمع انزام مغدهد را تعریف مرغ‬
‫پنمم‪.‬‬

‫‪>>> add = lambda x, y: x + y‬‬


‫)‪>>> add(5, 3‬‬
‫‪8‬‬

‫مغتوانمد همان تابع جمع را با پلمدواژه ‪ def‬تعریف پنمد‪ ،‬اما پمغ طوالنغتر خواهد‬
‫بود‪.‬‬

‫‪>>> def add(x, y):‬‬


‫‪return x + y‬‬
‫)‪>>> add(5, 3‬‬
‫‪8‬‬

‫حال ممان است پنزااو باشمد په "چرا در مورد المبداها این همره جنزرال وجرود‬
‫دارد؟ اگر آنها نسخه پمغ مختصر و مفمدتری از تعریف توابع با ‪ def‬هسرتند‪ ،‬پرس چره‬
‫مشالغ وجود دارد؟"‬
‫‪  82‬ترفندهای پایتون‬

‫به مثال زیر نگاهغ بمندازید و در حمن انزام این پار‪ ،‬عبارات بمران ترابع ‪1‬را در ذهرن‬
‫داشته باشمد‪.‬‬

‫)‪>>> (lambda x, y: x + y)(5, 3‬‬


‫‪8‬‬

‫بسمار خب‪ ،‬اینزا چه خبر است؟ فقط از المبدا برای تعریف ترابع جمرع درون خطرغ‬
‫استفاده پردم و بعد بالفاصله آن را با آرگومانهای ‪ 5‬و ‪ 3‬فراخوانغ پردم‪.‬‬
‫به لحاظ مفهومغ‪ ،‬عبارت المبدا ‪ lambda x, y: x + y‬همانند اعالم تابع با ‪ def‬است‪،‬‬
‫اما فقط درون خطغ نوشته شده است‪ .‬تفاوت اصلغ در اینزا ایرن اسرت پره الزم نمسرت‬
‫نامغ را برای تابع انتخاب پنم‪ .‬فقط گفتم په مغخواهم محاسبه سریعغ انزام دهم و بعد‬
‫بالفاصله آن را با فراخوانغ عبارت المبدا مانند تابع های عادی اجرا پردم‪.‬‬
‫پمی از پرداختن به مورد بعدی‪ ،‬ممان است بخواهمد پمغ در مورد مثرال قبرل فارر‬
‫پنمد تا واقعا مفهوم آن را دریابمد‪ .‬هنوز به یاد دارم په این پرار بررای مرن مردتغ طرول‬
‫پشمد‪ ،‬چون نمغتوانستم ذهنم را روی آن متمرپز پنم‪ .‬بنابراین نگران صرف چند دقمقه‬
‫در مفسر پایتون در این باره نباشمد‪ .‬این پار ارزشی را دارد‪.‬‬
‫تفاوت نحوی دیگری بمن تعاریف المبدا و تابع عادی وجرود دارد‪ .‬توابرع المبردا بره‬
‫عبارت محاسباتغ واحدی محدود مغشوند‪ .‬این بدان معنغ است په تابع المبدا نمغتواند‬
‫از جمالت ‪2‬یا حاشمهنویسغها‪( 3‬حتغ بعنوان خروجغ تابع) استفاده پند‪.‬‬
‫پس چگونه مغتوانمد مقادیر را از المبداها برگردانمرد؟ پرس از اجررای ترابع المبردا‪،‬‬
‫عبارت آن ارزیابغ مغشود و بعد به طور خودپار نتمزه عبرارت براز مغگرردد‪ ،‬بنرابراین‬
‫هممشه برگشت ضمنغ در این توابع وجود دارد‪ .‬به هممن دلمل است په برخرغ افرراد بره‬
‫المبداها توابع تک جملهای مغگویند‪.‬‬

‫‪1 Function expression‬‬


‫‪2 statements‬‬
‫‪3 annotations‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪83 ‬‬

‫‪ .1-2-3‬المبداهایی که میتوانید استفاده کنید‬


‫چه زمانغ باید از توابع المبدا در پد خود استفاده پنمد؟ به لحاظ فنغ‪ ،‬هر زمران پره‬
‫بخواهمد یک شغ سریع از تابع ناشناسغ بسازید‪ ،‬مغتوانمد از عبارت المبدا استفاده پنمد‬
‫و چون المبداها مغتوانند ناشناس باشند‪ ،‬پس لزومغ ندارد ابتدا آنها را نامگذاری پنمد‪.‬‬
‫این پار مغتواند ممانبری مفمد و "رمر بروپراتمک" برای تعریف تابع در پایتون ارائه‬
‫پند‪ .‬بمشترین موردی په ارلب برای المبداها باار مغبرریم‪ ،‬نوشرتن توابرع پوتراه بررای‬
‫مرتبسازی موارد قابل تارار با پلمد مشخص است‪.‬‬

‫])'‪>>> tuples = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c‬‬
‫)]‪>>> sorted(tuples, key=lambda x: x[1‬‬
‫])'‪[(4, 'a'), (2, 'b'), (3, 'c'), (1, 'd‬‬

‫در مثال فوق‪ ،‬لمستغ از ‪ tuple‬ها را با مقدار دوم (اندیس یک) در هرر ‪ tuple‬مرترب‬
‫مغپنمم‪ .‬در این حالت‪ ،‬تابع المبدا سریعا روشغ برای اصالح و مرتبسرازی فرراهم مرغ‬
‫پند‪ .‬در اینزا نمونه دیگری از مرتبسازی است په مغتوانمد در مورد آن فار پنمد‪.‬‬

‫)‪>>> sorted(range(-5, 6), key=lambda x: x * x‬‬


‫]‪[0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5‬‬

‫اممدوارم بتوانمد نحوه استفاده از المبدا بررای انعطرافپرذیری بمشرتر در محاسربات را‬
‫ببمنمد‪ .‬آیا مغخواهمد توالغ را با پلمدی په بصورت دلخواه محاسبه شرده مرتربسرازی‬
‫پنمد؟ مشالغ نمست‪ .‬حاال نحوه انزام این پار را مغدانمد‪.‬‬
‫ناته جالب دیگری درباره المبداها وجود دارد‪ :‬دقمقا مانند توابع تو در ترو‪ ،‬المبرداها‬
‫نمز مغتوانند همانند بستارهای معنایغ عمل پنند‪.‬‬
‫بستار معنایغ ‪1‬چمست؟ این فقط نامغ زیبا برای ترابعغ اسرت پره مقرادیر را از حروزه‬
‫محصور شده فراخوانغ مغپند‪ ،‬حتغ وقتغ په جریان برنامه دیگر در این حروزه نباشرد‪.‬‬
‫در اینزا مثال (نسبتا دانشگاهغ) برای توضمح این ایده ارائه شده است‪.‬‬

‫‪1 Lexical Closure‬‬


‫‪  84‬ترفندهای پایتون‬

‫‪>>> def make_adder(n):‬‬


‫‪return lambda x: x + n‬‬
‫)‪>>> plus_3 = make_adder(3‬‬
‫)‪>>> plus_5 = make_adder(5‬‬

‫)‪>>> plus_3(4‬‬
‫‪7‬‬
‫)‪>>> plus_5(4‬‬
‫‪9‬‬

‫در مثال فوق‪ ،‬المبدای ‪ x+n‬مغتوانرد بره مقردار ‪ n‬دسترسرغ پمردا پنرد اگرر در ترابع‬
‫‪( make_adder‬حوزه محصور) تعریف شده باشد‪.‬‬
‫گاهغ‪ ،‬استفاده از تابع المبدا بزای تابع تو در توی اعالم شرده برا پلمردواژه ‪ def‬مرغ‬
‫تواند هدف برنامهنویس را برا وضروح بمشرتری بمران پنرد‪ .‬صرادقانه بگرویم‪ ،‬ایرن اتفراق‬
‫معمولغ نمست‪ ،‬حداقل در سبک پدی په مایلم بنویسم‪ ،‬اینطور نمست‪ .‬پس پمغ بمشرتر‬
‫در مورد آن صحبت پنمم‪.‬‬

‫‪ .2-2-3‬شاید شما نباید ‪...‬‬


‫از یک طرف‪ ،‬اممدوارم په این فصل شما را به بررسغ توابع المبدای پایتون عالقمند‬
‫پند‪ .‬از طرف دیگر‪ ،‬احساس مغپنم وقت آن رسمده په این هشدار را بدهم په‪ :‬توابرع‬
‫المبدا باید پمتر و با احتماط بمشتری استفاده شوند‪.‬‬
‫مغدانم په قسمت زیادی از پد را با استفاده از المبداهایغ په "جالب" بره نظرر مرغ‬
‫رسند‪ ،‬نوشتهام‪ ،‬اما در واقع این وظمفه من و هماارانم بوده است‪ .‬اگر تررمب شدهاید ترا‬
‫از المبدا استفاده پنمد‪ ،‬چند انمه (یا چند دقمقه) وقت بگذارید تا فار پنمد پره آیرا ایرن‬
‫واقعا تممزترین و پایدارترین روش برای دستمابغ به نتمزه مطلوب است‪.‬‬
‫برای مثال‪ ،‬انزام پاری مانند این برای صرفهجرویغ در دو خرط پرد احمقانره اسرت‪.‬‬
‫قطعا‪ ،‬به لحاظ فنغ پار مغپند و "ترفند" بسمار مناسبغ است‪ ،‬اما افررادی پره در آینرده‬
‫قرار است در زمان پمرغ یرک مشرال را در ایرن پرد برطررف پننرد‪ ،‬بسرمار سرردرگم‬
‫خواهند شد‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪85 ‬‬

‫‪# Harmful:‬‬
‫‪>>> class Car:‬‬
‫)'!‪rev = lambda self: print('Wroom‬‬
‫)'!‪crash = lambda self: print('Boom‬‬

‫)(‪>>> my_car = Car‬‬


‫)(‪>>> my_car.crash‬‬
‫'!‪'Boom‬‬

‫در مورد ساختارهای پمچمده )(‪ map‬یا )(‪ filter‬برا اسرتفاده از المبرداهرا نمرز احسراس‬
‫مشابهغ دارم‪ .‬به خوانایغ دو قطعه پد زیر دقت پنمد و آنها را باهم مقایسه پنمد‪.‬‬

‫‪# Harmful:‬‬
‫)))‪>>> list(filter(lambda x: x % 2 == 0, range(16‬‬
‫]‪[0, 2, 4, 6, 8, 10, 12, 14‬‬

‫‪# Better:‬‬
‫]‪>>> [x for x in range(16) if x % 2 == 0‬‬
‫]‪[0, 2, 4, 6, 8, 10, 12, 14‬‬

‫اگر فار مغپنمد با استفاده از ‪ lambda‬ها پار پمچمدهای پردهاید و از این پار لذت‬
‫مغبرید‪ ،‬پمشنهاد مغپنم په این پار پمچمده را به صورت یک تابع عادی با خوانایغ پد‬
‫بمشتری انزام دهمد‪ .‬صرفهجرویغ در فشرردن پلمردها در درازمردت اهممترغ نردارد‪ ،‬امرا‬
‫همااران شما (و خود شما در آینده) پد تممز و خوانرا را بمشرتر از یرک جرادوگر مراهر‬
‫مغتوانمد تغممر دهمد‪.‬‬

‫‪ .3-2-3‬نکات کلیدی المبداها‬


‫‪ ‬توابع المبدا‪ ،‬توابع تک جملهای هستند په لزوما محدود به نرام نمسرتند (ناشرناس‬
‫هستند)‪.‬‬
‫‪ ‬توابع المبدا نمغتوانند از عبارات معمولغ پایتون اسرتفاده پننرد و هممشره شرامل‬
‫مقدار برگشتغ ضمنغ هستند‪.‬‬
‫‪  86‬ترفندهای پایتون‬

‫‪ ‬هممشه از خود بپرسمد‪ :‬آیا استفاده از توابع عادی با نام گذاری صحمح‪ ،‬خوانرایغ‬
‫بمشتری دارد؟ اگر پاس بله است‪ ،‬از المبدا استفاده نانمد‪.‬‬

‫‪1‬‬
‫‪ .3-3‬قدرت اعجاب انگیز دکوریتورها‬
‫تصور پنمد ‪ 30‬تابع با منطق های پمچمرده پسرب و پرار در برنامره خرود نوشرته ایرد‪.‬‬
‫در یک صبح شنبه بارانغ‪ ،‬رئمس پنار ممز شما مغآید و مغگوید‪:‬‬

‫“صبح شرنبه بخمرر! آن گزارشرهای ‪ TPS‬را بره خراطر داری؟ نمراز دارم ترا داده هرای‬
‫ورودی و خروجغ در هر مرحله تولمد گزارش را بره صرورت پامرل برت پنمرد‪ .‬بررای‬
‫اهداف حسابرسغ با شرپت ‪ XYZ‬ایرن اطالعرات بایرد آمراده شروند‪ .‬بره آنهرا گفرتم پره‬
‫مغتوانمم این پار را تا روز دوشنبه ارسال پنمم”‪.‬‬

‫این درخواست فشار خون شما را افزایی خواهد داد یا باعرث آرامری شرما خواهرد‬
‫شد‪ .‬بستگغ به این دارد په آیرا شرما از دپوریتورهرای پرایتون بره درک عممقرغ دسرت‬
‫یافته اید یا خمر‪ .‬بدون دپوریتورها‪ ،‬ممان است سه روز آینده را صرف اصالح هر یرک‬
‫از این ‪ 30‬تابع پنمد و با فراخوانغ های دستغ بت وقایع و گزارشگمری آنهرا را درهرم‬
‫بریزید‪ .‬لحظات سرگرم پننده ای است‪ ،‬اینطور نمست؟ با این حال‪ ،‬اگرر دپوریتورهرای‬
‫پایتون را بشناسمد‪ ،‬با آرامی به رئمس خود لبخند مغزنمد و مغگویمد‪:‬‬

‫“جمم نگران نباش‪ ،‬امروز تا ساعت ‪ 2‬بعد از ظهر این پار را انزام خواهم داد”‪.‬‬

‫درست پس از آن‪ ،‬پدی را برای دپوریتور عمومغ ‪ @audit_log‬تایرپ مرغپنمرد‬


‫(طول این پد تقریبا ‪ 10‬خط است) و به سرعت در جلوی تعریف تابع قرار مغدهمد‪ .‬بعد‬
‫پد خود را ارسال مغپنمد و یک فنزان قهوه دیگر مغخورید‪ .‬لذت بخی است‪ ،‬نه؟‬

‫‪1 Decorators‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪87 ‬‬

‫شاید در اینزا پمغ بزرگنمایغ مغپنم‪ .‬دپوریتورها مرغتواننرد قدرتمنرد و اعزراب‬


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

‫‪ .1-3-3‬قدرت دکوریتورها‬
‫دپوریتور پایتون به شما اماان گسترش و اصرالح رفتارهرای قابرل فراخروانغ (ماننرد‬
‫توابع و پالس ها) بدون اصالح دائمغ پارامتر قابل فراخوانغ را مغدهد‪ .‬هر فعرالمتغ پره‬
‫به اندازه پافغ عمومغ باشد و بتوانمد آن را به پالس یا رفتارهای موجرود اضرافه پنمرد‪،‬‬
‫یک مورد عالغ برای استفاده در دپوریتور محسوب مغشود‪ .‬پلمه انگلمسغ ‪Decorator‬‬
‫معادل فارسغ تزئمن است و در ادامه گاهغ از پلمه ترزئمن بره جرای دپوریترور اسرتفاده‬
‫خواهمم پرد‪.‬‬
‫به زبان ساده دپوریتورهای پایتون اماان اضافه پردن رفتار جدید به اشما را مغدهند‪.‬‬
‫این رفتارهای جدید در قالب بسته بندیهای جدید شغ خواهند بود‪.‬‬
‫پارهای تاراری په با استفاده از ‪ Decorator‬پرایتون مغتوانمرد انزرام دهمرد شرامل‬
‫موارد زیر است‪:‬‬
‫‪ o‬بت وقایع و گزارشگمری‬
‫‪ o‬اجرای پنترل دسترسغ و احراز هویت‬
‫‪ o‬تعممن زمان اجرای توابع‬
‫‪ o‬محدودیت های پراستفاده‬
‫‪( Caching o‬دخمره سازی موقت اطالعات) و…‬
‫‪  88‬ترفندهای پایتون‬

‫حال‪ ،‬چرا باید به پاربرد دپوریتورها در پایتون تسلط داشته باشمد؟ بدون درک ایرن‬
‫پاربرد چمزهایغ په در ابتدا گفتمم پامالد انتزاعغ به نظر مغرسد و فهممدن این موضرو‬
‫په دپوریتورها چقدر مغتوانند در پار روزانه یک توسعه دهنده پایتون سودمند باشند‪،‬‬
‫دشوار خواهد بود‪.‬‬
‫قطعا‪ ،‬فار پردن برای اولمن بار در مورد دپوریتورها پمغ دشوار است‪ ،‬اما ویژگرغ‬
‫بسمار سودمندی است په ارلب در فریمورک هرای شرخص الرث و در پتابخانره هرای‬
‫استاندارد پایتون با آن مواجه خواهمد شد‪ .‬در این مطلب نهایت تالشم را خواهم پرد ترا‬
‫بصورت گام به گام شما را با دپوریتور ها آشنا پنم و تعدادی از ویژگغ هرای اعزراب‬
‫انگمز دپوریتورها را ببمنمم‪.‬‬
‫قبل از بررسغ این موضو ‪ ،‬اپنون لحظه ای عالغ بررای یرادآوری در مرورد ویژگرغ‬
‫های توابع پالس اول (یا توابع به عنوان شهروندان درجه اول) در پایتون است‪ .‬مهمترین‬
‫ناات اساسغ “توابع پالس اول ”برای درک دپوریتورها عبارتند از‪:‬‬
‫‪ ‬توابع اشماء هستند ‪.‬توابع را مغتروان درون متغمرهرا ذخمرره پررد‪ ،‬یرا توابرع را بره‬
‫عنوان ورودی و خروجغ به توابع دیگر داد‪.‬‬
‫‪ ‬توابع را مغتوان در داخل توابع دیگر قرار داد ‪.‬تابع فرزنرد مغتوانرد از وضرعمت‬
‫محلغ تابع والد استفاده پند)‪. (lexical closures‬‬
‫خب‪ ،‬برای انزام این پار آماده هستمد؟ پس شرو پنمم‪.‬‬

‫‪ .2-3-3‬مبانی دکوریتورها‬
‫حاال‪ ،‬دپوریتورها واقعا چه چمزی هستند؟ دپوریتورها ترابع را ترزئمن یرا بسرتهبنردی‬
‫مغپنند و به شما اجازه مرغدهنرد ترا قبرل و بعرد از اجررای ترابع بسرتهبنردی شرده‪ ،‬پرد‬
‫دلخواهغ را اجرا پنمد‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪89 ‬‬

‫دپوریتور پایتون به شما اماان تعریف بلوک های قابل استفاده مزدد را مغدهد ترا‬
‫بتوانمد رفتار سایر توابع را تغممر یا گسترش دهمد‪ .‬رفتار تابع فقط هنگرام ترزئمن آن تغممرر‬
‫مغپند‪.‬‬
‫پماده سازی دپوریتور ساده چگونه است؟ به بمان ساده‪ ،‬دپوریتور پرایتون یرک ترابع‬
‫قابل فراخوانغ است په یک تابع قابل فراخروانغ دیگرری را بره عنروان ورودی دریافرت‬
‫مغپند و مغتواند آن را به عنوان خروجغ برگرداند‪.‬‬
‫تابع زیر دارای این ویژگغ است و مغتواند سادهترین دپوریتوری باشد په مغتوانمد‬
‫به صورت زیر بنویسمد‪.‬‬

‫‪def null_decorator(func):‬‬
‫‪return func‬‬

‫همانطور په مشاهده مغپنمد ‪ null_decorator‬یرک ترابع قابرل فراخروانغ اسرت‪ ،‬و‬


‫مقدار قابل فراخوانغ ‪ func‬په یک تابع است را به عنوان ورودی دریافت مغپند و آن‬
‫تابع را بدون ایزاد تغممری در خروجغ بر مغگرداند‪.‬‬
‫اجازه دهمد از آن برای تزئمن (بستهبندی ‪1‬یا ‪ )Decorate‬تابع دیگری استفاده پنمم‪.‬‬

‫‪>>> def greet():‬‬


‫'!‪return 'Hello‬‬

‫)‪>>> greet = null_decorator(greet‬‬

‫)(‪>>> greet‬‬
‫'!‪'Hello‬‬

‫در این مثال‪ ،‬ترابع ‪ greet‬را تعریرف پررده ام و بعرد بالفاصرله آن را از طریرق ترابع‬
‫‪null_decorator‬تزئمن پرده ام‪ .‬مغدانم په این موضو چندان مفمد به نظر نمغرسرد‪.‬‬
‫طراحغ پردهایرم ترا برغفایرده‬ ‫منظورم این است په دپوریتور تهغ را به گونهای خا‬

‫‪1 wrap‬‬
‫‪  90‬ترفندهای پایتون‬

‫باشد‪ ،‬درست است؟ این مثال نحوه پار سمنتاس دپوریتور پایتون برای حالرت ویرژه را‬
‫توضمح خواهد داد‪.‬‬
‫به جای فراخوانغ صرریح ‪ null_decorator‬روی ‪ greet‬و جرایگزینغ شرغ ‪، greet‬‬
‫مغتوانمد از @‪ decorator‬پایتون برای تزئمن مناسب تر و زیباتر تابع استفاده پنمد‪.‬‬

‫‪@null_decorator‬‬
‫‪def greet():‬‬
‫'!‪return 'Hello‬‬

‫)(‪>>> greet‬‬
‫'!‪'Hello‬‬

‫قرار دادن خط ‪ @null_decorator‬در مقابل تعریف تابع‪ ،‬همان چمزی است پره بره‬
‫عنوان اولمن تعریف تابع ارائه پرردیم و بعرد از طریرق دپوریترور ترابع را اجررا پرردیم‪.‬‬
‫استفاده از ‪ @decorator‬فقط فررم دگرگرون یافتره و ممرانبری بررای الگروی پرپراربرد‬
‫دپوریتور پایتون است‪.‬‬
‫توجه داشته باشمد پره اسرتفاده از ‪ @decorator‬ترابع را بالفاصرله در زمران تعریرف‬
‫تزئمن مغپند‪ .‬این سبب دسترسغ دشوار به نسخه اصرلغ ترزئمن نشرده مغشرود‪ .‬بنرابراین‬
‫ممان است برای حفظ اماان فراخوانغ تابع تزئمن نشده‪ ،‬گزینره هرای مختلفرغ را بررای‬
‫تزئمن دستغ انتخاب پنمد‪( .‬در ادامه در رابطه با این ویژگغ توضمحات بمشتری خرواهمم‬
‫داد(‬

‫‪ .3-3-3‬دکوریتورها رفتار توابع را اصالح میکنند‬


‫حاال په پمغ بمشرتر برا سرمنتاس دپوریترور آشرنا شردید‪ ،‬اجرازه دهمرد دپوریترور‬
‫دیگری بنویسمم په در واقع پاری را انزام مغدهد و رفتار ترابع ترزئمن شرده را اصرالح‬
‫مغپند‪ .‬در اینزا دپوریتور پمغ پمچمدهتری وجود دارد په نتمزه تابع تزئمن شده را بره‬
‫حروف بزرگ تبدیل مغپند‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪91 ‬‬

‫‪def uppercase(func):‬‬
‫‪def wrapper():‬‬
‫)(‪original_result = func‬‬
‫)(‪modified_result = original_result.upper‬‬
‫‪return modified_result‬‬
‫‪return wrapper‬‬

‫این دپوریتور بزای برگرداندن ورودی تابع مانند دپوریتور تهرغ‪ ،‬ترابع جدیردی را‬
‫‪ on-the-fly‬تعریف مغپند و از آن برای بستهبندی تابع ورودی استفاده مغپند تا تابع‪،‬‬
‫رفتار خود را در زمان فراخوانغ تغممر دهد‪ .‬تابع ‪ wrapper‬بره ترابع ورودی ترزئمن نشرده‬
‫دسترسغ دارد و مغتواند قطعه پدی را قبل و بعد از فراخوانغ تابع ورودی اجرا پند‪( .‬به‬
‫لحاظ فنغ‪ ،‬اصال نمازی به فراخوانغ تابع ورودی ندارد(‬
‫به این توجه داشته باشمد په چرا تابع تزئمن شده اجرا نشده است‪ .‬در واقع‪ ،‬فراخروانغ‬
‫تابع ورودی در این مرحله منطقغ نخواهد بود‪ .‬مغخواهمم وقتغ په دپوریتور در نهایت‬
‫فراخوانغ مغشود‪ ،‬بتواند رفتار تابع ورودی خود را اصالح پند‪.‬‬
‫ممان است بخواهمد یاغ دو دقمقه در مورد آن فار پنمد‪ .‬مغدانرم پره ایرن مسرئله‬
‫چقدر مغتواند دشوار به نظر برسد‪ ،‬اما قول مغدهم په با پمک یاردیگر ایرن مسرئله را‬
‫حل خواهمم پرد‪ .‬وقرت آن رسرمده پره در عمرل دپوریترور پرایتون پره حرروف را بره‬
‫حروف بزرگ تبدیل مغپند را ببمنمم‪ .‬اگر با استفاده از آن ترابع اصرلغ ‪ greet‬را ترزئمن‬
‫پنمد‪ ،‬چه چمزی رخ خواهد داد؟‬

‫‪@uppercase‬‬
‫‪def greet():‬‬
‫'!‪return 'Hello‬‬

‫)(‪>>> greet‬‬
‫'!‪'HELLO‬‬

‫اممدوارم این همان نتمزه ای په انتظار آن را داشتمد‪ ،‬باشد‪ .‬اجازه دهمد نگاهغ دقمقتر‬
‫به آنچه په در اینزا اتفاق افتاده‪ ،‬بماندازیم‪.‬‬
‫‪  92‬ترفندهای پایتون‬

‫برخالف ‪ ،null_decorator‬دپوریتور ‪ @upercase‬هنگامغ په تابع را ترزئمن مرغ‬


‫پند‪ ،‬شغء متفاوتغ از تابع را بر مغگرداند‪.‬‬

‫‪>>> greet‬‬
‫>‪<function greet at 0x10e9f0950‬‬

‫)‪>>> null_decorator(greet‬‬
‫>‪<function greet at 0x10e9f0950‬‬

‫)‪>>> uppercase(greet‬‬
‫>‪<function uppercase.<locals>.wrapper at 0x76da02f28‬‬

‫در اینزا ناته ی پوچاغ وجود دارد‪ ،‬دپوریتور پایتون باید این پرار را انزرام دهرد‬
‫تررا در هنگررام فراخرروانغ نهررایغ‪ ،‬رفتررار تررابع تررزئمن شررده را اصررالح پنررد ‪.‬دپوریتررور‬
‫‪ @upercase‬خود نمز تابع اسرت ‪.‬تنهرا روش بررای ا رگرذاری برر “رفترار آترغ ” ترابع‬
‫ورودی په آن را تزئمن مغپند‪ ،‬جایگزینغ (یا بستهبندی) تابع ورودی است‪.‬‬
‫به هممن دلمل‪ ،‬تابع ‪ uppercase‬تابع دیگری را په بعدا مغتروان فراخروانغ پررد‪ ،‬را‬
‫تعریف پرده و برمغگرداند‪ ،‬تابع ورودی اصلغ را اجرا پرده و نتمزه آن را اصالح مرغ‬
‫پند‪.‬‬
‫دپوریتورها رفتار تابع قابل فراخوانغ را از طریق بستار بسته سراز )‪(lexical closure‬‬
‫اصالح مغپنند‪ ،‬پس نباید تابع اصلغ را به صورت دائم اصالح پنند ‪.‬تابع قابل فراخوانغ‬
‫اصلغ به صورت دائمغ اصالح نمغشود بلاه فقط رفتار آن فقط هنگام فراخروانغ تغممرر‬
‫مغپند‪.‬‬
‫این پار به شما اماان اضافه پردن بلوک های ساختاری قابل اسرتفاده مزردد‪ ،‬ماننرد‬
‫بت وقایع و گزارش گمری و سایر ابزارهای دقمق‪ ،‬به توابع و پالسرهای موجرود را مرغ‬
‫دهد ‪.‬این پار دپوریتورها را به چنان ویژگغ قدرتمندی در پایتون تبردیل مرغپنرد پره‬
‫ارلب در پتابخانه استاندارد و پتابخانههای شخص الث مورد استفاده قرار مغگمرد‪.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪93 ‬‬

‫‪ .4-3-3‬یک وقفه کوتاه برای درک بهتر دکوریتورها‬


‫اگر احساس مغپنمد در این مرحله به اندازه یک فنزان قهوه یا پماده روی در اطراف‬
‫ممز پارتان نماز دارید‪ ،‬این پامالد طبمعغ اسرت ‪.‬بره نظرر مرن‪ ،‬درک مفهروم دپوریتورهرا‬
‫یاغ از دشوارترین پارها در پایتون است‪.‬‬
‫لطفا عزله نانمد و نگران فهممدن جواب مسئله نباشمد‪ .‬تاه پدهای بررسغ شده را به‬
‫صورت جداگانه در یک مفسر پایتون اجرا پنمد و نتمزه ی آن را مشاهده پنمد ترا بهترر‬
‫درک پنمد‪ .‬مغدانم په مغتوانمد این پار را انزام دهمد!‬

‫‪ .5-3-3‬استفاده از چند دکوریتور در یک تابع‬


‫شاید تعزب آور نباشد په مغتوانمد بمی از یک دپوریتور را روی تابع اعمال پنمد‪.‬‬
‫این پار ا رات آنها را انباشته مغپند و این همان چمرزی اسرت پره دپوریتورهرا را بره‬
‫اندازه بلوک های چندبخشغ قابل استفاده مزدد سودمند مغپند‪.‬‬
‫مثالغ در این مورد بررسغ خواهمم پرد‪ .‬دو دپوریتور زیر‪ ،‬رشته خروجغ تابع تزئمن‬
‫شده با برچسبهای ‪ HTML‬را بسته بندی مغپنند‪ .‬با نگاهغ بر نحوه قرارگمری تو در توی‬
‫تگ ها‪ ،‬مغتروان فهممرد پره دپوریترور پرایتون از چره فرآینردی بررای اعمرال چنردین‬
‫دپوریتور استفاده مغپند‪.‬‬

‫‪def strong(func):‬‬
‫‪def wrapper():‬‬
‫'>‪return '<strong>' + func() + '</strong‬‬
‫‪return wrapper‬‬

‫‪def emphasis(func):‬‬
‫‪def wrapper():‬‬
‫'>‪return '<em>' + func() + '</em‬‬
‫‪return wrapper‬‬
‫‪  94‬ترفندهای پایتون‬

‫حال اجازه دهمد این دو دپوریترور را انتخراب پنرمم و آنهرا را در ترابع ‪ greet‬خرود‬
‫اعمال پنمم‪ .‬مغتوانمد از ‪ @syntax‬معمولغ برای آن استفاده پنمد و چنردین دپوریترور‬
‫را در باالی یک تابع واحد انباشته پنمد‪.‬‬

‫‪@strong‬‬
‫‪@emphasis‬‬
‫‪def greet():‬‬
‫'!‪return 'Hello‬‬

‫در صررورت اجرررای تررابع تررزیمن شررده‪ ،‬انتظررار داریررد پرردام ورودی را ببمنمررد؟ آیررا‬
‫دپوریتور ‪ @emphasis‬ابتدا تگ >‪ <em‬خود را اضرافه خواهرد پررد‪ ،‬یرا ‪@strong‬‬
‫اولویت دارد؟ هنگامغ په تابع تزیمن شده را فراخوانغ مغپنمد‪ ،‬چه اتفراقغ رخ خواهرد‬
‫داد؟‬

‫)(‪>>> greet‬‬
‫'>‪'<strong><em>Hello!</em></strong‬‬

‫این نتمزه به وضوح نشان مغدهد په دپوریتورهای چنرد گانره چگونره از پرایمن بره‬
‫باال عمل پردهاند‪ .‬ابتدا تابع ورودی با دپوریترور ‪ @emphasis‬بسرته بنردی شرد و بعرد‬
‫تابع تزئمن شده حاصل با دپوریتور ‪ @strong‬دوباره بسته بندی شد‪.‬‬
‫برای ایناه رفتار پرایمن بره براال در حافظره تران براقغ بمانرد ممخرواهم طریقره اجررای‬
‫دپوریتورهای چندگانه را در هسته پایتون توضمح دهم ‪.‬دپوریتورهای پایتون با اسرتفاده‬
‫از پشته پماده سازی مغشود و ما این رفتار را ‪ decorator stacking‬مغناممم ‪.‬اولمن پشته‬
‫را در پایمن ایزاد مغپنمم و بعد بلوک های پد جدید را از باال به آن اضافه مغپنمم ترا‬
‫اجرای برنامه راه خود را از پایمن به سمت باال پمی ببرد‪.‬‬
‫اگر مثال باال را تززیه پنمد و از ‪ @syntax‬برای اعمرال بره دپوریتورهرا خرودداری‬
‫پنمد‪ ،‬زنزمره فراخوانغ های مربوط به تابع دپوریتور به این شال خواهد بود‪.‬‬

‫))‪decorated_greet = strong(emphasis(greet‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪95 ‬‬

‫همانطور په مغبمنمد ابتدا دپوریتور ‪ emphasis‬اعمال مغشود و بعد تابع بسته بندی‬
‫شده حاصل دوباره با دپوریتور ‪ strong‬بسته بندی مغشود‪.‬‬
‫این همچنمن به این معنغ است په در نهایت سطوح عممق پشرته سرازی دپوریتورهرا‬
‫بر ممزان پارایغ ا ر برنامه خواهند گذاشت‪ ،‬چون مدام فراخوانغ هرای توابرع ترو در ترو‬
‫انزام مغدهند‪ .‬در عمل‪ ،‬معموال انزام این پار مشال نخواهد بود‪ ،‬اما اگر پرارایغ پرد‬
‫نوشته شده برایتان مهم است باید مرتبه زمانغ عملمات ‪ decoration‬را در نظر بگمرید‪.‬‬

‫‪ .6-3-3‬دکوریت توابعی که آرگومانهای متعددی میپذیرند‬


‫تاپنون همه نمونه ها فقط تابع ساده ‪ greet‬په همک آرگومانغ ندارد را ترزئمن پررده‬
‫اند‪ .‬تا این لحظه‪ ،‬دپوریتورهایغ په در این مطلب دیدیرد الزم نمسرت پره برا آرگومران‬
‫های ارسال به تابع ورودی سر و پار داشته باشند‪ .‬اگرر یارغ از ایرن دپوریتورهرا را بره‬
‫تابعغ په آرگومان هایغ را مغگمرد اعمال پنمد‪ ،‬پارپرد درستغ نخواهد داشت‪.‬‬
‫چگونه تابعغ په آرگومانهای دلخواه مغگمرد را تزئمن مغپنند؟‬
‫اینزاست په به ویژگغ ‪ *args‬و ‪ **kwargs‬پایتون برای مقابله با تعرداد متغمرهرای‬
‫آرگومان احتماج خواهمم داشت‪ .‬دپوریترور پرایتون زیرر بره اسرم ‪ proxy‬از ایرن مزیرت‬
‫استفاده مغپند‪.‬‬

‫‪def proxy(func):‬‬
‫‪def wrapper(*args, **kwargs):‬‬
‫)‪return func(*args, **kwargs‬‬
‫‪return wrapper‬‬

‫دو مورد قابل توجه در مورد این دپوریتور وجود دارد‪:‬‬


‫‪ ‬با استفاده از عملگرهای * و ** در ورودی تابع ‪ ،wrapper‬همه آرگومان هرای‬
‫ورودی را جمع آوری مغپند و آنها را به صورت شغ ‪ args‬و ‪ kwargs‬ذخمره‬
‫مغپند‪.‬‬
‫‪  96‬ترفندهای پایتون‬

‫‪ ‬سپس تابع ‪ wrapper‬آرگومان های جمع آوری شده توسط عملاررد * و ** را‬
‫بازگشایغ مغپند ‪1‬و آن را به تابع ورودی ارسال مغپند‪.‬‬
‫اممدوارم ایده اصلغ مسئله را متوجه شده باشمد هرچند مغدانم پمغ نماز به تمرین برا‬
‫پدها دارید تا ایده اصلغ را به خوبغ متوجه شوید‪.‬‬
‫اجازه دهمد روش گذاشته شده با دپوریتور پراپسغ را در مثالغ عملغ تر بسط دهمم‪.‬‬
‫در اینزا دپوریتور ‪ trace‬را خواهمم سراخت پره آرگومران هرای ورودی ترابع و نترایج‬
‫حاصل از آن را در طغ زمان اجرا بت مغپند‪.‬‬

‫‪def trace(func):‬‬
‫‪def wrapper(*args, **kwargs):‬‬
‫' )(}__‪print(f'TRACE: calling {func.__name‬‬
‫)'}‪f'with {args}, {kwargs‬‬

‫)‪original_result = func(*args, **kwargs‬‬

‫' )(}__‪print(f'TRACE: {func.__name‬‬


‫)'}‪f'returned {original_result!r‬‬

‫‪return original_result‬‬

‫‪return wrapper‬‬

‫تزئمن تابع با تابع ‪ trace‬و بعد فراخروانغ آن‪ ،‬آرگومران هرای منتقرل شرده بره ترابع و‬
‫مقدار برگشتغ آن را چاپ خواهد پرد‪.‬‬
‫این پار گاهغ اوقات به عنوان یک پمک عالغ برای خطایابغ محسوب مغشود‪.‬‬

‫‪1 Argument unpacking‬‬


‫فصل سوم‪ :‬توابع تأثیرگذار ‪97 ‬‬

‫‪@trace‬‬
‫‪def say(name, line):‬‬
‫'}‪return f'{name}: {line‬‬

‫)'‪>>> say('Jane', 'Hello, World‬‬


‫‪'TRACE: calling say() with ("Jane", "Hello, World"),‬‬
‫'}{‬
‫'"‪'TRACE: say() returned "Jane: Hello, World‬‬
‫'‪'Jane: Hello, World‬‬

‫در رابطه با خطایرابغ صرحبت پرردیم‪ ،‬مرواردی وجرود دارنرد پره هنگرام خطایرابغ‬
‫دپوریتورها باید به خاطر داشته باشمد‪.‬‬

‫‪ .7-3-3‬نوشتن دکوریتورهای قابل دیباگ‬


‫هنگام استفاده از دپوریتور‪ ،‬در واقع تنها پاری په انزام مغدهمرد جرایگزین پرردن‬
‫یک تابع با ترابع دیگرر اسرت ‪.‬یرک ناتره منفرغ ایرن فرآینرد ایرن اسرت پره برخرغ از‬
‫‪metadata‬هررای تررابع اصررلغ تررزئمن شررده مخفررغ و جررایگزین مغشرروند! ایررن یاررغ از‬
‫پنهانترین عملاردهای دپوریتورهای پایتون است په ممان است درنظر گرفته نشود‪.‬‬
‫برای مثرال‪ ،‬نرام ترابع اصرلغ‪ ،‬داکاسرترینگ آن و لمسرت پارامترهرای ترابع پرس از‬
‫دپوریت تغممر خواهند پرد! به قطعه پد زیر توجه پنمد‪.‬‬

‫‪def greet():‬‬
‫"""‪"""Return a friendly greeting.‬‬
‫'!‪return 'Hello‬‬

‫)‪decorated_greet = uppercase(greet‬‬

‫در صورت دسترسغ به هریک از ‪ metadata‬هرای ترابع ‪ greet‬پرس از ‪decorate‬‬


‫شدن‪ metadata ،‬مربوط به دپوریتور را مشاهده خواهمد پرد په تغممر پردهاند‪.‬‬
‫‪  98‬ترفندهای پایتون‬

‫__‪>>> greet.__name‬‬
‫'‪'greet‬‬
‫__‪>>> greet.__doc‬‬
‫'‪'Return a friendly greeting.‬‬

‫__‪>>> decorated_greet.__name‬‬
‫'‪'wrapper‬‬
‫__‪>>> decorated_greet.__doc‬‬
‫‪None‬‬

‫این پار خطایابغ و پار با مفسر پایتون را چالی برانگمز مغپنرد ‪.‬خوشربختانه‪ ،‬بررای‬
‫این مشال راهااری سریع به نام دستور ‪ functools.wraps‬وجود دارد په در پتابخانره‬
‫استاندارد پایتون گنزانده شده است‪.‬‬
‫مغتوانمرد از دسرتور ‪ functools.wraps‬در دپوریتورهرای خرود اسرتفاده پنمرد ترا‬
‫‪metadata‬های مفقود شده را از تابع تزئمن نشده به تابع تزئمن شده پپغ پنمد‪ .‬مثالغ را‬
‫باهم بررسغ مغپنمم‪.‬‬

‫‪import functools‬‬
‫‪def uppercase(func):‬‬
‫)‪@functools.wraps(func‬‬
‫‪def wrapper():‬‬
‫)(‪return func().upper‬‬
‫‪return wrapper‬‬

‫استفاده از دستور ‪ functools.wraps‬در دپوریتور باعث مغشرود ‪ metadata‬هرای‬


‫مختلررف هنگررام دپوریررت شرردن تررابع تغممررری نانررد و مقررادیری ماننررد نررام تررابع و‬
‫داکاسترینگ تابع به صورت دست نخورده باقغ بماند‪ .‬حقه ی جالبمست نه؟‬

‫‪@uppercase‬‬
‫‪def greet():‬‬
‫"""‪"""Return a friendly greeting.‬‬
‫'!‪return 'Hello‬‬

‫__‪>>> greet.__name‬‬
‫'‪'greet‬‬
‫__‪>>> greet.__doc‬‬
‫'‪'Return a friendly greeting.‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪99 ‬‬

‫به عنوان بهترین روش‪ ،‬توصمه مغپنم په در تمام دپوریتورهایغ په مغنویسرمد‪ ،‬از‬
‫دستور ‪ functools.wraps‬استفاده پنمد‪ .‬این پار زیاد طول نمغپشد ولغ باعث مغشود‬
‫شما و بقمه از دردسرهای خطایابغ در دپورتمورها در امان بمانمد‪.‬‬
‫تبریک مغگویم‪ ،‬شما به انتهای این مطلب در رابطه با دپوریتورهای پایتون رسمدید‬
‫و احتماال درباره دپوریتورهای موجود در پایتون چمزهای زیادی آموختره ایرد‪ .‬پارتران‬
‫عالغ بود!‬

‫‪ .8-3-3‬نکات کلیدی دکوریتورهای پایتون‬


‫‪ ‬دپوریتورها‪ ،‬بلوک های پد قابرل اسرتفاده مزردد هسرتند پره مرغتوانمرد بررای‬
‫اصالح رفتار یک تابع از آن استفاده پنمد بدون ایناه نمراز بره تغممرر دائمرغ ترابع‬
‫باشد‪.‬‬
‫‪ ‬دستور ‪ @decorator‬تنها شال پوتاه نویسغ شرده بررای فراخروانغ دپوریترور‬
‫روی تابع ورودی است ‪.‬دپوریتورهای متعدد روی ترابع واحرد از پرایمن بره براال‬
‫اعمال مغشوند ‪ decorator stacking‬را به خاطر داشته باشمد‪.‬‬
‫‪ ‬به عنوان بهترین شموه خطایابغ‪ ،‬از دستور پمک پننده ‪ functools.wraps‬روی‬
‫دپوریتورهای خود استفاده پنمرد ترا ‪ metadata‬هرای بمشرتری را از ترابع قابرل‬
‫فراخروانغ ترزئمن نشرده )‪ (undecorated‬بره ترابع قابرل فراخروانغ ترزئمن شرده‬
‫)‪(decorated‬منتقل پنمد‪.‬‬
‫‪ ‬دپوریتورها درست ماننرد هرر ابرزار دیگرری در جعبره ابرزار توسرعه نررم افرزار‪،‬‬
‫نوشدارو یا عالج قطعغ نمستند و نباید از آنها بمی از حد استفاده پررد‪ .‬متعرادل‬
‫سازی نمازها بر حسب اولویت انزام پارها با هدف گرفترار نشردن در آشرفتگغ‬
‫وحشتناک و رمرقابل نگهداری پد منبع بسمار اهممت دارد‪.‬‬
‫‪  100‬ترفندهای پایتون‬

‫‪ .4-3‬کاربردهای جالب ‪ *args‬و ‪**kwargs‬‬

‫زمانغ همراه با پایتونمستغ باهوش په اصطالح او "‪ "argh‬و "‪" kwargh‬برود‪ ،‬برنامره‬
‫نویسرغ دو نفررره انزرام مررغدادم‪ .‬او هرر بررار تعریرف ترابع را بررا پارامترهرای اختمرراری یررا‬
‫پلمدواژهای تایپ مغپرد‪ .‬ما در پنار یادیگر به بمنی دیگری از پایتون دست یافتمم‪.‬‬
‫پارامترهررای ‪ *args‬و ‪ **kwargs‬ویژگررغ بس رمار مفمرردی در پررایتون هسررتند و درک‬
‫توانمندی آنها‪ ،‬شما را به توسعهدهنده مو رتری مبدل خواهد پرد‪.‬‬

‫‪ .1-4-3‬پارامترهای ‪ *args‬و ‪ **kwargs‬برای چه مواردی استفاده میشوند؟‬


‫این پارامترها به تابع اجازه مغدهند تا آرگومرانهرای اختمراری بپرذیرد‪ ،‬بنرابراین مرغ‬
‫توانمد رابط های انعطافپذیری را در ماژولها و پالسهای پایتونغ خود ایزاد پنمد‪.‬‬

‫‪def foo(required, *args, **kwargs):‬‬


‫)‪print(required‬‬
‫‪if args:‬‬
‫)‪print(args‬‬
‫‪if kwargs:‬‬
‫)‪print(kwargs‬‬

‫تابع فوق حداقل به یک آرگومران بره نرام "‪ "required‬احتمراج دارد‪ ،‬امرا مرغتوانرد‬
‫آرگومانهای موقعمتغ و پلمدواژهای اختماری دیگری را نمز بپذیرد‪.‬‬
‫اگر تابع را با آرگومانهای ‪1‬بمشتری فراخوانغ پنمم‪ args ،‬آرگومانهای اضافغ را به‬
‫صورت ‪ tuple‬جمعآوری خواهد پرد‪ .‬پمشوند * باید در ابتدای نرام آرگومران باشرد ترا‬
‫این پار را انزام دهد‪.‬‬
‫همچنمن‪ kwargs ،‬آرگومرانهرای پلمردواژهای ‪ 2‬اضرافه را بره صرورت ‪dictionary‬‬
‫جمعآوری خواهد پررد‪ ،‬چرون نرام پرارامتر دارای پمشروند ** اسرت‪ .‬اگرر هرمکیرک از‬

‫‪1 Arguments‬‬
‫‪2 Keyword arguments‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪101 ‬‬

‫آرگومانهای اضافغ به تابع منتقرل نشروند‪ ،‬هرر دوی ‪ args‬و ‪ kwargs‬مرغتواننرد خرالغ‬
‫باشند‪.‬‬
‫چون این تابع را با ترپمب متفاوتغ از آرگومانها فراخوانغ مرغپنرمم‪ ،‬خواهمرد دیرد‬
‫په چگونه پایتون آنها را مطابق با ایناه آرگومانهای اختماری ‪ args‬یا ‪ kwargs‬هستند‪،‬‬
‫درون پارامترهای ‪ args‬و ‪ kwargs‬به صورت ‪ tuple‬و ‪ dictionary‬جمعآوری مغپند‪.‬‬

‫)(‪>>> foo‬‬
‫‪TypeError:‬‬
‫"'‪"foo() missing 1 required positional arg: 'required‬‬

‫)'‪>>> foo('hello‬‬
‫‪hello‬‬

‫)‪>>> foo('hello', 1, 2, 3‬‬


‫‪hello‬‬
‫)‪(1, 2, 3‬‬

‫)‪>>> foo('hello', 1, 2, 3, key1='value', key2=999‬‬


‫‪hello‬‬
‫)‪(1, 2, 3‬‬
‫}‪{'key1': 'value', 'key2': 999‬‬

‫مغخواهم این ناته را تصریح پنم په فراخوانغ پارامترهرای ‪ args‬و ‪ kwargs‬صررفا‬


‫یک قرارداد نامگذاری است‪ .‬در مثال قبلغ اگر آنها را به صرورت ‪ *parms‬و ‪**argv‬‬
‫فراخوانغ پنمد نمزپار خواهد پرد‪ .‬سمنتاس واقعغ به ترتمب بصورت تکستاره (*) یرا‬
‫دوستاره (**) است‪ .‬مهم نمست چه نامغ برای آرگومانها انتخاب مغپنمد‪.‬‬
‫با این حال‪ ،‬توصمه مغپنم برای پرهمز از اشتباهات‪ ،‬قرارداد نامگذاری پذیرفتره شرده‬
‫را رعایت پنمد و هر از گاهغ برای فراخوانغ از نامهرای"‪ "argh‬و "‪ "kwargh‬اسرتفاده‬
‫پنمد‪ .‬شاید جالب باشد!‬
‫‪  102‬ترفندهای پایتون‬

‫‪ .2-4-3‬ارسال آرگومانهای اختیاری یا کلیدواژهای‬


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

‫‪def foo(x, *args, **kwargs):‬‬


‫'‪kwargs['name'] = 'Alice‬‬
‫) ‪new_args = args + ('extra',‬‬
‫)‪bar(x, *new_args, **kwargs‬‬

‫این روش جالب مغتواند در ارث بری‪ ،‬ارتباط جالبغ بمن پالس فرزند و پالس پدر‬
‫برقرار پند‪ .‬شما مغتوانمد از این ترفند بررای اضرافه پرردن یرک رفترار بره پرالس پردر‬
‫استفاده پنمد بدون ایناه امضای مترد سرازنده پرالس پردر را تغممرر دهمرد‪ .‬ایرن روشرغ‬
‫پاربردی برای زمانمست په ممان است از یک ‪ API‬استفاده پنمد په بدون پنترل شما‪،‬‬
‫تغممر خواهد پرد‪.‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫‪class AlwaysBlueCar(Car):‬‬
‫‪def __init__(self, *args, **kwargs):‬‬
‫)‪super().__init__(*args, **kwargs‬‬
‫'‪self.color = 'blue‬‬

‫‪>>> AlwaysBlueCar('green', 48392).color‬‬


‫'‪'blue‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪103 ‬‬

‫متد سازنده پالس ‪ AlwaysBlueCar‬به سادگغ تمام آرگومانها را بره پرالس والرد‬
‫خود منتقل مغپند‪ .‬این بدان معناست په اگر متد سازنده پالس والد تغممر پند‪ ،‬پالس‬
‫‪ AlwaysBlueCar‬طبق انتظار پار خواهد پرد‪.‬‬
‫عمب این روش اینزاست په اپنون متد سازنده ‪ AlwaysBlueCar‬امضای تقریبا برغ‬
‫فایردهای دارد‪ .‬نمغترروانمم بردون مشرراهده پررالس والرد‪ ،‬بفهمررمم آرگومانهررای ورودی‬
‫پالس ‪ AlwaysBlueCar‬چه چمزی هستند‪.‬‬
‫معموال از این روش در سلسله مراتب پالسهای پد خود استفاده نخواهمد پرد‪ .‬این‬
‫پار گمج پننده خواهد بود‪ .‬سناریوی محتمل این است په مغخواهمد رفتار را در برخغ‬
‫از پالسهای خارجغ په خارج از پنترل شما هستند‪ ،‬اصالح پرده یا تحتالشعا قررار‬
‫دهمد‪.‬‬
‫اما این پار همواره سناریو خطرناپغ است‪ ،‬پس بهتر است مراقب باشمد‪.‬‬
‫یک سناریو دیگر په در آن احتماال این روش مفمد است‪ ،‬نوشتن توابع ‪ wrapper‬از‬
‫قبمل دپوریتورها است‪ .‬در آنزا معموال مغخواهمد آرگومرانهرای دلخرواه پره بره ترابع‬
‫‪ wrapper‬منتقل شده را نمز بپذیرید‪.‬‬
‫اگر بتوانمم بدون نماز به تغممر امضای تابع اصلغ این پار را انزام دهرمم‪ ،‬پرد شرما در‬
‫آینده قابلمت نگهداری بهتری خواهد داشت‪.‬‬
‫‪  104‬ترفندهای پایتون‬

‫‪def trace(f):‬‬
‫)‪@functools.wraps(f‬‬
‫‪def decorated_function(*args, **kwargs):‬‬
‫)‪print(f, args, kwargs‬‬
‫)‪result = f(*args, **kwargs‬‬
‫)‪print(result‬‬
‫‪return decorated_function‬‬

‫‪@trace‬‬
‫‪def greet(greeting, name):‬‬
‫)‪return '{}, {}!'.format(greeting, name‬‬

‫)'‪>>> greet('Hello', 'Bob‬‬


‫}{ )'‪<function greet at 0x1031c9158> ('Hello', 'Bob‬‬
‫'!‪'Hello, Bob‬‬

‫همانطور په مشاهده پردید‪ ،‬با ایرن روش توانسرتمم پارامترهرای اختمراری را بره ترابع‬
‫دپوریتور منتقل پنمم‪ .‬گاهغ از این روش به منظور رعایت اصل ‪1 DRY‬اسرتفاده خواهرد‬
‫شد‪.‬‬

‫‪ .3-4-3‬نکات کلیدی ‪ *args‬و ‪**kwargs‬‬

‫‪ ‬دسرررتورهای ‪ *args‬و ‪ **kwargs‬بررره شرررما اماررران نوشرررتن توابرررع برررا تعرررداد‬


‫آرگومانهای متغمر در پایتون را مغدهد‪.‬‬
‫‪ ‬دستور ‪ *args‬آرگومانهای مروقعمتغ اضرافغ را بره صرورت ‪ tuple‬جمرعآوری‬
‫مغپند و دستور ‪ **kwargs‬آرگومانهرای پلمردواژهای اضرافغ را بره صرورت‬
‫‪ dictionary‬جمعآوری مغپند‪.‬‬
‫‪ ‬سمنتاس واقعغ * و ** است‪ .‬فراخوانغ آنها با دستورهای ‪ args‬و ‪ kwargs‬فقط‬
‫یک قرارداد است (و بهتر است آن را رعایت پنمد)‪.‬‬

‫‪1 Don’t Repeat Yourself‬‬


‫فصل سوم‪ :‬توابع تأثیرگذار ‪105 ‬‬

‫‪ .5-3‬بازکردن آرگومانهای توابع‬


‫ویژگغ بسمار جالب اما پنهان‪ ،‬قابلمت براز پرردن بسرتهبنردی آرگومرانهرای ترابع برا‬
‫استفاده از عملگرهای * و ** است‪ .‬اجازه دهمد تابعغ ساده را برای مثال تعریف پنمم‪.‬‬

‫‪def print_vector(x, y, z):‬‬


‫))‪print('<%s, %s, %s>' % (x, y, z‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬این تابع سه آرگومان (‪ y ،x‬و ‪ )z‬را مغگمرد و آنهرا را‬
‫به روشغ زیبا چاپ مغپند‪ .‬ممان است از این تابع بررای چراپ بردارهرای ‪ 3‬بعردی در‬
‫برنامه خود استفاده پنمم‪.‬‬

‫)‪>>> print_vector(0, 1, 0‬‬


‫>‪<0, 1, 0‬‬

‫حال بسته به نو ساختار دادهای په برای نمرایی بردارهرای سره بعردی انتخراب مرغ‬
‫پنمم‪ ،‬چاپ آنها با تابع ‪ print_vector‬ممان است پمغ ناخوشایند باشد‪ .‬بررای مثرال‪،‬‬
‫اگر بردارهای خود را به صورت تاپلها یا لمستها مقدار دهغ پنمم‪ ،‬هنگام چاپ آنها‪،‬‬
‫باید به طور صریح‪ ،‬اندیس هر مولفه را مشخص پنمم‪.‬‬

‫)‪>>> tuple_vec = (1, 0, 1‬‬


‫]‪>>> list_vec = [1, 0, 1‬‬
‫‪>>> print_vector(tuple_vec[0],‬‬
‫‪tuple_vec[1],‬‬
‫)]‪tuple_vec[2‬‬

‫>‪<1, 0, 1‬‬

‫فراخوانغ معمولغ تابع با آرگومانهای جداگانه‪ ،‬طوالنغ و رمرضروری بره نظرر مرغ‬
‫رسد‪ .‬اگر بتوانمم شغء وپتور را به سه مولفه آن "تززیه پنمم" و همه چمز را بره یابراره‬
‫به تابع ‪ print_vector‬منتقل پنمم‪ ،‬ظریفتر نخواهد بود؟‬
‫‪  106‬ترفندهای پایتون‬

‫البته‪ ،‬به سادگغ مغتوانمرد ترابع ‪ print_vector‬را مزرددا تعریرف پنمرد ترا پرارامتر‬
‫واحدی با نام شغء وپتور را به عنوان ورودی دریافت پند (امرا بخراطر رعایرت صصرول‬
‫یک مثال ساده‪ ،‬فعال این گزینه را نادیده مغگمریم‪).‬‬
‫خوشبختانه‪ ،‬روش بهتری برای پرداختن به این وضعمت در پرایتون تحرت عنروان براز‬
‫پردن بستهبندی آرگومان تابع‪ 1‬با استفاده از عملگر * وجود دارد‪.‬‬

‫>>>‬ ‫)‪print_vector(*tuple_vec‬‬
‫‪<1,‬‬ ‫>‪0, 1‬‬
‫>>>‬ ‫)‪print_vector(*list_vec‬‬
‫‪<1,‬‬ ‫>‪0, 1‬‬

‫قرار دادن عالمت * قبل از یک شغ قابل تارار ‪2‬در فراخوانغ تابع‪ ،‬آن را باز خواهد‬
‫پرد و عناصر آن را بصورت آرگومانهرای مروقعمتغ جداگانره بره ترابع فراخروانغ شرده‬
‫منتقل خواهد پرد‪.‬‬
‫این تانمک برای هر گونه تابع تارارپذیر‪ ،‬از جمله عبارات مولد ‪ 3‬پارساز اسرت‪ .‬برا‬
‫استفاده از عملگر * روی مولد‪ ،‬تمام عناصر مولد را اجرا مغپند و آنها را به ترابع منتقرل‬
‫مغپند‪.‬‬

‫))‪>>> genexpr = (x * x for x in range(3‬‬


‫)‪>>> print_vector(*genexpr‬‬

‫عالوه بر عملگر * برای باز پردن توالغهایغ ماننرد تاپرلهرا‪ ،‬لمسرتهرا و مولردها در‬
‫داخل آرگومانهای موقعمتغ‪ ،‬عملگر ** برای باز پردن آرگومرانهرای پلمردواژهای از‬
‫دیاشنری وجود دارد‪ .‬تصور پنمد وپتور ما بصورت شغء دیاشنری زیر تعریرف شرده‬
‫است‪.‬‬

‫}‪>>> dict_vec = {'y': 0, 'z': 1, 'x': 1‬‬

‫‪1 Function Argument Unpacking‬‬


‫‪2 Iterable‬‬
‫‪3 Generator expresions‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪107 ‬‬

‫مررغترروانمم بررا اسررتفاده از عملگررر ** برررای برراز پررردن‪ ،‬ایررن دیاشررنری را برره تررابع‬
‫‪ print_vector‬منتقل پنمم‪.‬‬

‫)‪>>> print_vector(**dict_vec‬‬
‫> ‪< 1, 0, 1‬‬

‫چون دیاشنریها بدون ترتمب هستند‪ ،‬پس مقادیر دیاشنری و آرگومانهای تابع برر‬
‫پایه پلمدهای دیاشنری تطبمق داده مغشوند‪ .‬آرگومان ‪ x‬مقردار مررتبط برا پلمرد '‪ 'x‬در‬
‫دیاشنری را دریافت مغپند‪.‬‬
‫اگر بخواهمد از عملگر تک ستاره (*) برای باز پردن بستهبنردی دیاشرنری اسرتفاده‬
‫پنمد‪ ،‬پلمدها به صورت ترتمب تصادفغ‪ ،‬به این تابع منتقل خواهند شد‪.‬‬

‫)‪>>> print_vector(*dict_vec‬‬
‫> ‪< y, x, z‬‬

‫ویژگغ باز پردن بستهبندی آرگومان تابع در پرایتون‪ ،‬انعطراف پرذیری زیرادی را در‬
‫اختمار شما قرار مغدهد‪ .‬ارلب این بدان معنغ است په شما نمازی به پمادهسرازی پرالس‬
‫برای نو دادههای مورد نماز با برنامه خود ندارید‪ .‬در نتمزه‪ ،‬استفاده از ساختار دادههرای‬
‫درونغ ساده مانند تاپلها یا لمستها پافغ خواهد بود و بره پراهی پمچمردگغ پرد شرما‬
‫پمک خواهد پرد‪.‬‬

‫‪ .1-5-3‬نکات اصلی در هنگام بازکردن آرگومانهای تابع‬


‫‪ ‬از عملگرهای * و ** مغتوان برای "باز پردن" آرگومانهای تابع از توالغها و‬
‫دیاشنریها استفاده پرد‪.‬‬
‫‪ ‬استفاده مو ر از باز پردن بستهبندی آرگومانها مغتواند به شما در نوشرتن رابرط‬
‫های بسمار انعظافپذیر برای ماژولها و توابع پمک پند‪.‬‬
‫‪  108‬ترفندهای پایتون‬

‫‪ .6-3‬هیچ چیز برای ‪Return‬‬

‫پایتون برگشت ضمنغ ‪ None‬را به پایان هر تابع اضافه مرغپنرد‪ .‬بنرابراین‪ ،‬اگرر ترابع‬
‫مقدار برگشتغ را مشخص ناند‪ ،‬تابع به صورت پمیفرض ‪ None‬را برمغگرداند‪.‬‬
‫این بدان معناست په مغتوانمد بمانمههرای برگشرتغ ‪ None‬را برا بمانمرههرای برگشرتغ‬
‫خالغ جایگزین پنمد و یا آنها را پامالد خالغ نگه دارید و همچنان نتمزه مشابه را بدست‬
‫آورید‪.‬‬

‫‪def foo1(value):‬‬
‫‪if value:‬‬
‫‪return value‬‬
‫‪else:‬‬
‫‪return None‬‬

‫‪def foo2(value):‬‬
‫"""`‪"""Bare return statement implies `return None‬‬
‫‪if value:‬‬
‫‪return value‬‬
‫‪else:‬‬
‫‪return‬‬

‫‪def foo3(value):‬‬
‫‪"""Missing return statement implies `return‬‬
‫"""`‪None‬‬
‫‪if value:‬‬
‫‪return value‬‬

‫اگر مقداری اشتباه را به عنوان آرگومانهای تابع ارسال پنمد‪ ،‬هر سه تابع به درسرتغ‬
‫‪ None‬را بر مغگردانند‪.‬‬

‫))‪>>> type(foo1(0‬‬
‫>'‪<class 'NoneType‬‬

‫))‪>>> type(foo2(0‬‬
‫>'‪<class 'NoneType‬‬

‫))‪>>> type(foo3(0‬‬
‫>'‪<class 'NoneType‬‬
‫فصل سوم‪ :‬توابع تأثیرگذار ‪109 ‬‬

‫حال‪ ،‬چه زمانغ استفاده از این ویژگغ در پد پایتون شما ایده خوبغ است؟‬
‫قاعده سرانگشتغ من این است په اگر تابع مقدار برگشتغ نداشته باشد (سرایر زبانهرا‬
‫این روش را ‪ procedure‬مغنامند)‪ ،‬آنگاه با خروجغ تابع پاری ندارم‪ .‬افزودن ‪return‬‬
‫خالغ فقط پار اضافغ و باعث سردرگمغ خواهد بود‪ .‬مثالغ برای این روش‪ ،‬ترابع ‪print‬‬
‫داخلغ پایتون است په فقط برای پارهای جانبغ (چاپ متن) فراخوانغ مغشود و هرگز‬
‫با مقدار برگشتغ آن پاری نداریم‪.‬‬
‫اجازه دهمد تابعغ مانند ‪ sum‬په جزو توابع داخلغ پایتون است را انتخاب پنمم‪ .‬ایرن‬
‫پار به وضوح دارای مقدار برگشت منطقغ است و معموال تابع ‪ sum‬فقط برای پارهای‬
‫جانبغ آن فراخوانغ نخواهد شد‪ .‬هدف آن جمع پرردن اعرداد برا یاردیگر و بعرد ارائره‬
‫نتمزه است‪ .‬حال اگر تابع از نقطه نظر منطقغ دارای مقدار برگشرتغ باشرد‪ ،‬بایرد تصرممم‬
‫بگمرید په آیا از ‪ return‬استفاده مغپنمد یا خمر‪.‬‬
‫از یک طرف‪ ،‬مغتوانمد استدالل پنمد په حذف ‪ return None‬پد را دقمقتر مرغ‬
‫پند و در نتمزه خواندن و درک آن راحت تر خواهد بود‪ .‬به لحاظ ذهنغ‪ ،‬ممان اسرت‬
‫بگویمد په پد را "زیباتر" نمز مغپند‪.‬‬
‫از طرف دیگر‪ ،‬ممان است برخغ برنامهنویسان از چنمن رفتاری در پرایتون متعزرب‬
‫شوند‪ .‬اگر زمان نوشتن پد تممز و قابل نگهداری برسد‪ ،‬رفتار تعزبآور به ندرت نشرانه‬
‫خوبغ است‪.‬‬
‫در نهایت‪ ،‬به طور مستمر ایمملهایغ را دریافرت پرردم پره بره مرن" بمانمره برگشرت‬
‫ضمنغ" در مثال پد را خاطر نشان مغپردند‪ .‬رفتار برگشرت ضرمنغ پرایتون بره وضروح‬
‫برای همه مشخص نبود‪ .‬یادداشتغ به آن اضافه پردم ترا مشرخص شرود پره چره اتفراقغ‬
‫افتاده و بعد ایمملها متوقف شدند‪.‬‬
‫اشتباه برداشت نانمد‪ ،‬بمشتر از هر پسغ نوشتن پد تممرز و "زیبرا" را دوسرت دارم و‬
‫نمز قویا احساس مغپنم په برنامهنویسها باید از جزئمات پامل زبانغ په با آن پار مرغ‬
‫پنند‪ ،‬اطالعات الزم را داشته باشند‪.‬‬
‫‪  110‬ترفندهای پایتون‬

‫اما وقتغ تا مر نگهداری چنمن سوء تفاهمهای سادهای را در نظر بگمرید‪ ،‬منطقغ است‬
‫په به سمت نوشتن پد صریح و خوانا متمایل شوید‪ .‬رمر از اینها‪ ،‬پرد وسرمله برقرراری‬
‫ارتباط است‪.‬‬

‫‪ .1-6-3‬نکات کلیدی ‪ return None‬در پایتون‬


‫‪ ‬اگر تابع مقدار برگشتغ را مشخص ناند‪ ،‬مقدار ‪ None‬را برمغگرداند‪ .‬ایناه آیا‬
‫صریحا بنویسمد ‪ return None‬بستگغ به تصرممم خودتران و سربک پدنویسرغ‬
‫شما دارد‪.‬‬
‫‪ ‬این ویژگغ هسته پایتون است‪ .‬پد شما ممان است با نوشرتن ‪ return None‬در‬
‫خروجغ توابعغ په خروجغ ندارند‪ ،‬از ابهرام جلروگمری پنرد‪ .‬یرا ممارن اسرت‬
‫باعث تعزب برنامه نویسان شود‪ .‬تصممم به عهده خودتران و سربک پرد نویسرغ‬
‫شماست‪.‬‬
‫فصل چهارم‪ :‬پالسها و برنامهنویسغ شغء گرا‬

‫فصل چهارم‬

‫کالسها و برنامهنویسی‬
‫شیء گرا‬
‫‪ .1-4‬مقایسه شیء ها‪ "is" :‬در مقابل "=="‬
‫وقتغ بچه بودم‪ ،‬همسایههای ما دو گربه دوقلو داشتند‪ .‬آنها ظاهرا مشابه بودنرد‪ ،‬خرز‬
‫زرالغ و چشمهای سبز تمرزبمن مشرابهغ داشرتند‪ .‬اگرر برخرغ از ویژگرغهرای شخصرمتغ‬
‫گربهها پنار گذاشته شوند‪ ،‬نمغتوانستمد با نگاه پردن به آنها تفاوتشان را بگویمد‪ .‬البته‪،‬‬
‫آنها دو گربه متفاوت بودند‪ ،‬دو موجود جداگانه‪ ،‬با وجود ایناه دقمقرا یاسران بره نظرر‬
‫مغرسمدند‪.‬‬
‫این پار در من تفاوت در معنایغ بمن برابرر و مشرابه را القراء پررد و ایرن تفراوت در‬
‫درک نحوه رفتار عملگرهای مقایسه ‪ is‬و == در پایتون حائز اهممت است‪.‬‬
‫عملگر == با بررسغ برابری مقایسه مغپند‪ :‬اگر این گربهها اشماء پایتون بودنرد و آن‬
‫ها را با عملگر == مقایسه مغپردیم‪ ،‬در پاس "هر دو گربه برابر هستند"را بدست مرغ‪-‬‬
‫آوردیم‪.‬‬
‫با این حال‪ ،‬عملگر ‪ ،is‬هویت ها را با هم مقایسه مغپند‪ :‬اگرر گربرههرای خرود را برا‬
‫عملگر ‪ is‬مقایسه مغپردیم‪ ،‬در پاسر "ایرن دو گربره متفراوت هسرتند" را بدسرت مرغ‬
‫آوردیم‪.‬‬
‫قبل از ایناه گرفتار این قماس پالفه پننده گربه شروم‪ ،‬اجرازه دهمرد یرک تاره پرد‬
‫پایتون واقعغ را بررسغ پنمم‪.‬‬
‫ابتدا شغء جدید لمست را ایزاد مغپنمم و آن نامگذاری مغپنمم‪ ،‬و بعد متغمر دیگرر‬
‫(‪ )b‬په به همان شغء لمست اشاره دارد را تعریف مغپنمم‪.‬‬

‫]‪>>> a = [1, 2, 3‬‬


‫‪>>> b = a‬‬
‫‪  114‬ترفندهای پایتون‬

‫اجازه دهمد این دو متغمر را بررسغ پنمم‪ .‬مغتوانمم مشاهده پنمم په آنها بره لمسرت‬
‫های در ظاهر یاسان اشاره مغپنند‪.‬‬

‫>>>‬ ‫‪a‬‬
‫‪[1,‬‬ ‫]‪2, 3‬‬
‫>>>‬ ‫‪b‬‬
‫‪[1,‬‬ ‫]‪2, 3‬‬

‫چون دو شغء لمست یاسان به نظر مغرسند‪ ،‬پس هنگامغ په آنها را بررای برابرری‬
‫با استفاده از عملگر == مقایسه مغپنمم‪ ،‬نتمزه صحمح را بدست خواهمم آورد‪.‬‬

‫‪>>> a == b‬‬
‫‪True‬‬

‫با این حال‪ ،‬این به ما نمغگوید په ‪ a‬و ‪ b‬در واقع به شرغء یاسران اشراره مرغپننرد‪.‬‬
‫البته‪ ،‬مغدانمم په این اشما چگونه مقداردهغ شدهاند‪ ،‬اما فرض پنمد په این را نمغدانمم‪.‬‬
‫چگونه مغتوانمم بفهممم په این دو اشما هویت یاسان دارند؟‬
‫پاس ‪ ،‬مقایسه هر دو متغمر با عملگر ‪ is‬است‪ .‬این عملگر تایمد مغپند په هر دو شرغ‬
‫در واقع به یک لمست اشاره مغپنند‪.‬‬

‫‪>>> a is b‬‬
‫‪True‬‬

‫اجازه دهمد ببمنمم هنگام ایزاد پپغ مشابهغ از شغء لمست‪ ،‬چه اتفاقغ مرغافترد‪ .‬مرغ‬
‫توانمم این پار با فراخوانغ متد )(‪ list‬در لمست موجود‪ ،‬برای ایزاد یک پپغ په آن را ‪c‬‬
‫مغناممم‪ ،‬انزام دهمم‪.‬‬

‫)‪>>> c = list(a‬‬

‫از طرف دیگر‪ ،‬خواهمد دید په لمست جدیدی په ایزاد پردیم پامالد مشابه با شرغء‬
‫لمست اشاره شده با ‪ a‬و ‪ b‬است‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪115 ‬‬

‫‪>>> c‬‬
‫]‪[1, 2, 3‬‬

‫حاال اینزاست په موضو جالب مغشود‪ .‬اجازه دهمد پپغ لمست ‪ c‬را با لمست اولمره‬
‫‪ a‬با استفاده از عملگر == مقایسه پنمم‪ .‬انتظار دارید چه پاسخغ را مشاهده پنمد؟‬

‫‪>>> a == c‬‬
‫‪True‬‬

‫خب‪ ،‬اممدوارم این همان چمزی باشد په انتظار آن را داشتمد‪ .‬آنچه این نتمزره بره مرا‬
‫مغگوید این است په ‪ c‬و ‪ a‬محتویات مشابهغ دارند‪ .‬آنها در پایتون برابر در نظر گرفته‬
‫مغشوند‪ .‬اما آیا در واقع آنها به شغء مشابهغ اشاره مغپنند؟ اجازه دهمد برا عملگرر ‪is‬‬
‫جواب را پمدا پنمم‪.‬‬

‫‪>>> a is c‬‬
‫‪False‬‬

‫بوم! اینزاست په نتمزه متفاوتغ را بدست مغآوریم‪ .‬پایتون مغگوید په متغمرهای ‪c‬‬
‫و ‪ a‬به دو شغء متفاوت اشاره مغپنند‪ ،‬حتغ اگر محتویات آنها یاسان باشند‪.‬‬
‫پس‪ ،‬برای یادآوری‪ ،‬اجازه دهمد تفاوت بمن ‪ is‬و == را در دو تعریف پوتاه خالصره‬
‫پنمم‪.‬‬
‫‪ ‬عبارت ‪ is‬زمانغ برابر با ‪ True‬خواهد شرد پره دو شرغء هویرت یاسرانغ داشرته‬
‫باشند‪( .‬به یک ماان از حافظه اشاره پنند‪).‬‬
‫‪ ‬عبارت == زمانغ برابر با ‪ True‬خواهد شد په دو شغء محتویات یاسانغ داشرته‬
‫باشند‪.‬‬
‫فقط به یاد داشته باشمد په هر زمان بخواهمد بمن استفاده از ‪ is‬و == در پایتون تصممم‬
‫بگمرید‪ ،‬گربههای دوقلو را تصور پنمد (در مورد سگها هم عمل مغپند)‪ .‬اگر این پار‬
‫را انزام دهمد‪ ،‬مشالغ نخواهمد داشت‪.‬‬
‫‪  116‬ترفندهای پایتون‬

‫‪ .2-4‬تبدیل رشته (هر کالس به __‪ __repr‬نیاز دارد)‬


‫وقتغ پالس سفارشغ را در پایتون تعریف پرده و بعد یاغ از نمونههای آن را روی‬
‫پنسول چاپ مغپنمد (یا آن را در مفسر بررسرغ مرغپنمرد)‪ ،‬نتمزرهای نسربتا ناخوشرایند‬
‫بدست مغآوریرد‪ .‬رفترار تبردیل پرمیفررض ‪ to_string‬در پرالسهرا سراده اسرت امرا‬
‫توضمحات زیادی در رابطه با آن وجود ندارد‪.‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫)‪>>> my_car = Car('red', 37281‬‬


‫)‪>>> print(my_car‬‬
‫>‪<__console__.Car object at 0x109b73da0‬‬
‫‪>>> my_car‬‬
‫>‪<__console__.Car object at 0x109b73da0‬‬

‫بطور پمیفرض‪ ،‬تمام آن چمزی په بدست مرغآوریرد‪ ،‬رشرته حراوی نرام پرالس و‬
‫شناسه نمونه شغء (په آدرس حافظه شغء در ‪ CPython‬است) مغباشد‪ .‬این بهتر از همک‬
‫است‪ ،‬اما چندان مفمد نمست‪.‬‬
‫ممان است پمرغ روی ایرن شرغء پرار پررده باشرمد و برا اسرتفاده از پرینرت یرک‬
‫خروجغ برای شغء خود درنظر بگمرید یا حتغ یک متد اختصاصغ )(‪ to_string‬نوشرته‬
‫باشمد په این پار را مغپند‪.‬‬

‫)‪>>> print(my_car.color, my_car.mileage‬‬


‫‪red 37281‬‬

‫به جای نوشرتن یرک مترد )(‪ to_string‬سفارشرغ‪ ،‬بهترر اسرت متردهای __‪ __str‬و‬
‫__‪ __repr‬را به پالس خود اضافه پنمد‪ .‬آنها روش پایتونغ برای پنترل نحوه تبردیل‬
‫اشماء به رشتهها در موقعمتهای مختلف هستند‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪117 ‬‬

‫اجازه دهمد نحوه پار این روشها را در عمل بررسغ پنمم‪ .‬برای شرو ‪ ،‬مغخرواهمم‬
‫متد __‪ __str‬به پالس ‪ Car‬په قبال تعریف پردهایم را اضافه پنمم‪.‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫‪def __str__(self):‬‬
‫'‪return f'a {self.color} car‬‬

‫وقتغ نمونه پالس ‪ Car‬را چاپ مغپنمد‪ ،‬نتمزهای متفاوت و پمغ بهبودیافته بدست‬
‫خواهمد آورد‪.‬‬

‫)‪>>> my_car = Car('red', 37281‬‬


‫)‪>>> print(my_car‬‬
‫'‪'a red car‬‬
‫‪>>> my_car‬‬
‫>‪<__console__.Car object at 0x109ca24e0‬‬

‫بررسغ شغء ‪ Car‬در پنسول هنوز نتمزه قبلغ را په حاوی شناسه شغء است را به مرا‬
‫مغدهد‪ .‬اما چاپ شغء منزر به بازگشت رشته با متد __‪ __str‬په اضرافه پرردیم‪ ،‬مرغ‬
‫شود‪.‬‬
‫متد __‪ __str‬یاغ از متدهای داندر پایتون است و وقتغ مغخواهمد یک شغء را به‬
‫رشته تبدیل پنمد‪ ،‬به صورت خودپار فراخوانغ مغشود‪.‬‬

‫)‪>>> print(my_car‬‬
‫‪a red car‬‬

‫)‪>>> str(my_car‬‬
‫'‪'a red car‬‬

‫)‪>>> '{}'.format(my_car‬‬
‫'‪'a red car‬‬
‫‪  118‬ترفندهای پایتون‬

‫با پمادهسازی مناسب __‪ ،__str‬دیگر نمازی نمست نگران چاپ مستقمم ویژگغهرای‬
‫شغء یا نوشتن تابع جداگانه )(‪ to_string‬باشمد‪ .‬این روش پرایتونغ بررای پنتررل تبردیل‬
‫شغء به رشته است‪.‬‬
‫به هر حال‪ ،‬برخغ افراد متدهای "داندر" پایتون را "متدهای جادویغ" مرغنامنرد‪ .‬امرا‬
‫این متدها به همک وجه جادویغ نمسرتند‪ .‬واقعمرت ایرن اسرت پره ایرن متردها بره صرورت‬
‫زیرخطهای دوتایغ شرو شده و به پایان مغرسند‪ ،‬په صرفا قوانمن نامگذاری اسرت ترا‬
‫متوجه شویم این متدها‪ ،‬متدهای هسته پایتون هستند‪ .‬همچنمن این پار جلوی تداخل نرام‬
‫این متدها‪ ،‬با متدهای اختصاصغ برنامه شما را مغگمرد‪ .‬شغء سرازنده __‪ __init‬نمرز از‬
‫قوانمن مشابهغ پمروی مغپند و همک چمز جادویغ یا سری در مورد آن وجود ندارد‪.‬‬
‫از استفاده از متدهای داندر پایتون نترسمد‪ ،‬اینها به شما پمک مغپنند‪.‬‬

‫‪ .1-2-4‬متد __‪ __str‬در مقایسه با __‪__repr‬‬

‫داستان تبدیل رشته ما به اینزا ختم نمغشود‪ .‬آیا نحوه بررسغ ‪ my_car‬در مفسر پره‬
‫هنوز نتمزه عزمب >‪ <Car object at 0x109ca24e0‬را مغدهد را مغبمنمد؟‬
‫سبب وقو این امر این بود په در واقع دو روش داندر وجود دارد پره نحروه تبردیل‬
‫اشماء به رشتهها در پرایتون ‪ 3‬را پنتررل مرغپنرد‪ .‬روش اول __‪__str‬اسرت و اطالعرات‬
‫پمغ در مورد آن دارید‪ .‬روش دوم __‪ __repr‬است و نحوه پار با آن شبمه به __‪__str‬‬
‫اسرت‪ ،‬امرا در موقعمرت هرای مختلرف اسرتفاده مرغ شرود‪( .‬پرایتون ‪ 2.x‬نمرز دارای روش‬
‫__‪ __unicode‬است په بعدا پمغ به این موضو مغپردازم‪).‬‬
‫در اینزا آزمایی سادهای وجود دارد په مغتوانمد در هنگام استفاده از __‪ __str‬یا‬
‫__‪ __repr‬برای درک این ایده باار گمرید‪ .‬اجازه دهمرد دوبراره پرالس ‪ Car‬خرود را‬
‫تعریف پنمم‪ ،‬پس این پالس حاوی هر دو متد داندر برای تبدیل رشته خواهد بود‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪119 ‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫‪def __repr__(self):‬‬
‫'‪return '__repr__ for Car‬‬

‫‪def __str__(self):‬‬
‫'‪return '__str__ for Car‬‬

‫حال‪ ،‬وقتغ از طریق نمونههای قبلغ پد را اجرا مغپنمد‪ ،‬مغفهممرد پره پردام روش‬
‫نتمزه تبدیل رشته را در هر مورد پنترل مغپند‪.‬‬

‫)‪>>> my_car = Car('red', 37281‬‬


‫)‪>>> print(my_car‬‬
‫‪__str__ for Car‬‬
‫)‪>>> '{}'.format(my_car‬‬
‫'‪'__str__ for Car‬‬
‫‪>>> my_car‬‬
‫‪__repr__ for Car‬‬

‫این آزمایی تایمرد مرغپنرد پره بررسرغ یرک شرغء در مفسرر پرایتون صررفا نتمزره‬
‫__‪ __repr‬شغء را چاپ مغپند‪.‬‬
‫جالب ایناه مزموعههایغ مانند لمستها و دیاشنریها همواره از نتمزه __‪__repr‬‬
‫برای نمایی اشماء حاوی آن استفاده مغپنند‪ ،‬حترغ اگرر ‪ str‬را روی یرک مزموعرهای‬
‫همانند لمست فراخوانغ پنمد‪.‬‬

‫)]‪str([my_car‬‬
‫']‪'[__repr__ for Car‬‬

‫برای انتخاب دستغ بمن دو روش تبدیل رشته‪ ،‬برای بمان دقمقتر هدف پد خود‪ ،‬بهتر‬
‫است از توابع داخلغ پایتون )(‪ str‬و )(‪ repr‬استفاده پنمد‪ .‬استفاده از آنهرا برر فراخروانغ‬
‫مستقمم __‪ __str‬یا __‪ __repr‬ترجمح داده مغشود‪ ،‬چون بهتر به نظر مغرسد و نتمزره‬
‫مشابهغ را مغدهد‪.‬‬
‫‪  120‬ترفندهای پایتون‬

‫)‪>>> str(my_car‬‬
‫'‪'__str__ for Car‬‬
‫)‪>>> repr(my_car‬‬
‫'‪'__repr__ for Car‬‬

‫حتغ با این تحقمق پامل‪ ،‬ممان است متعزب شوید په تفاوت واقعغ برمن __‪__str‬‬
‫و __‪ __repr‬در چمست‪ .‬اینگونه به نظر مغرسد په هر دو هردف مشرابهغ را دنبرال مرغ‬
‫پنند‪ ،‬بنابراین ممان است مشخص نباشد په چه زمانغ از پدام باید استفاده پنمد‪.‬‬
‫با پرسمدن سؤاالتغ ماننرد ایرن‪ ،‬معمروال بهتررین ایرده‪ ،‬بررسرغ ایرن مرورد اسرت پره‬
‫پتابخانه استاندارد پایتون چه پاری انزام مغدهد‪ .‬وقت این است په آزمرایی دیگرری‬
‫را انزام دهمم‪ .‬یک شغء ‪ datetime.date‬را ایزاد پرده و خواهمم فهممد په چگونه از‬
‫__‪ __repr‬و __‪ __str‬برای پنترل تبدیل رشته استفاده مغپند‪.‬‬

‫‪>>> import datetime‬‬


‫)(‪>>> today = datetime.date.today‬‬

‫نتمزه تابع __‪ __str‬شغء تاری در پایتون باید خوانا باشد‪ .‬ایرن بره معنرای برگشرت‬
‫نمایی متنغ دقمق برای برنامهنویسان است (چمزی په شما با آن احساس راحتغ مغپنمد‬
‫را نشان مغدهد‪ ).‬بنابراین‪ ،‬هنگام فراخوانغ با متد )(‪ str‬در مورد شغء تاری ‪ ،‬چمزی شبمه‬
‫به فرمت تاری استاندارد را بدست مغآوریم‪.‬‬

‫)‪>>> str(today‬‬
‫'‪'2017-02-02‬‬

‫با متد داندر __‪ ،__repr‬معتقدیم په مهمتر از همره‪ ،‬نتمزره بایرد بردون ابهرام باشرد‪.‬‬
‫رشته حاصل بمشتر به عنوان پمک به اشاالزدایغ برای توسعهدهنردگان در نظرر گرفتره‬
‫شده است و به هممن خاطر باید در مورد محتویات این شغء‪ ،‬تا حد اماان صریح باشرد‪.‬‬
‫به هممن دلمرل نتمزره دقمرقترری بدسرت خواهمرد آورد اگرر مترد )(‪ repr‬را بررای شرغء‬
‫فراخوانغ پنمد‪ .‬این نتمزه حتغ شامل نام پامل ماژول و نام پالس نمز مغباشد‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪121 ‬‬

‫)‪>>> repr(today‬‬
‫')‪'datetime.date(2017, 2, 2‬‬

‫مغتوانمم رشته برگشت داده شرده را برا __‪ __repr‬پپرغ و پمسرت پنرمم و آن را بره‬
‫عنوان پد معتبر پایتون اجررا پنرمم ترا شرغء اصرلغ تراری را دوبراره ایزراد پنرمم‪ .‬ایرن‬
‫رویاردی عالغ برای نوشتن ‪ repr‬های اختصاصغ است‪.‬‬
‫از طرف دیگر‪ ،‬درمغیرابمم پره نوشرتن __‪ __repr‬هرای اختصاصرغ پمرغ دشروار‬
‫است‪ .‬معموال فار مغپنمد ارزش این همه زحمت را نخواهد داشت و فقط پرار اضرافغ‬
‫به شما تحممل مغپند‪ .‬قاعده سرانگشتغ من این است په رشتههای __‪ __repr‬را بدون‬
‫ابهام و مفمد برای توسعهدهندگان تولمد پنم‪ ،‬اما انتظار ندارم آنها بتوانند شغء پاملغ را‬
‫براساس خروجغ من تولمد پنند‪.‬‬

‫‪ .2-2-4‬چرا هر کالس به __‪ __repr‬نیاز دارد‬


‫اگر مترد __‪ __str‬را اضرافه نانمرد‪ ،‬پرایتون هنگرام جسرتزوی __‪ __str‬بره نتمزره‬
‫__‪ __repr‬متوسل مغشود‪ .‬بنابراین‪ ،‬توصمه من این است په هممشه حرداقل یرک مترد‬
‫__‪ __repr‬را به پالسهای خود اضافه پنمد‪ .‬این پار تقریبا در همه موارد‪ ،‬برا حرداقل‬
‫پار اجرایغ‪ ،‬نتمزه تبدیل رشتهای مفمد را تضممن خواهد پرد‪.‬‬
‫در اینزا نحوه افزودن سریع و پارآمد تبدیل رشرته بره پرالسهرای شرما ارائره شرده‬
‫است‪ .‬برای پالس ‪ Car‬ممان است با متد __‪ __repr‬زیر شرو پنمم‪.‬‬

‫‪def __repr__(self):‬‬
‫')}‪return f'Car({self.color!r}, {self.mileage!r‬‬

‫لطفا توجه داشته باشمد په من از پرچم ‪1‬تبدیل ‪ !r‬استفاده مغپنم تا مطمئن شروم پره‬
‫رشرررته خروجرررغ بزرررای )‪ str(self.color‬و )‪ str(self.mileage‬از )‪ rep(self.color‬و‬
‫)‪ rep(self.mileage‬استفاده مغپند‪.‬‬

‫‪1 Flag‬‬
‫‪  122‬ترفندهای پایتون‬

‫این پار خوب جواب مغدهد‪ ،‬اما تنها عمب آن این است په نام پالس داخل رشرته‬
‫را تارار مغپنمم‪ .‬ترفندی په مغتوانمد در اینزا برای جلوگمری از ایرن تاررار اسرتفاده‬
‫پنمد‪ ،‬استفاده از ویژگغ __‪ __class __.__ name‬شغء است په هممشه نام پالس را‬
‫بصورت رشته بمان خواهد پرد‪.‬‬
‫مزیت این روش این است په هنگام تغممرر نرام پرالس‪ ،‬پمرادهسرازی __‪ __repr‬را‬
‫اصالح نخواهمد پرد‪ .‬این پار سبب پایبندی راحت به اصل خودت را تارار نارن‪ 1‬مرغ‬
‫شود‪.‬‬

‫‪def __repr__(self):‬‬
‫'(}__‪return (f'{self.__class__.__name‬‬
‫)')}‪f'{self.color!r}, {self.mileage!r‬‬

‫ناته منفغ این اجراء این است په رشته فرمت پامالد طوالنغ و سرنگمن اسرت‪ .‬امرا برا‬
‫فرمتدهغ دقمق‪ ،‬مغتوانمد پد را مطابق با پمشنهاد ‪ PEP 8‬بهبود دهمد‪.‬‬
‫با اجرای متد __‪ __repr‬فوق‪ ،‬هنگام بررسغ شغء یا فراخوانغ مستقمم )(‪ repr‬روی‬
‫آن نتمزه مفمدی را بدست مغآوریم‪.‬‬

‫)‪>>> repr(my_car‬‬
‫')‪'Car(red, 37281‬‬

‫چاپ شغء یا فراخوانغ )(‪ str‬روی آن همان رشته را برمغگرداند چون هنگام اجرای‬
‫متد __‪ __str‬به صورت خودپار متد __‪ __repr‬را فراخوانغ مغپند‪.‬‬

‫)‪>>> print(my_car‬‬
‫')‪'Car(red, 37281‬‬
‫)‪>>> str(my_car‬‬
‫')‪'Car(red, 37281‬‬

‫بر این باورم په این رویارد با پمترین ممرزان پرار اجرایرغ‪ ،‬برا ارزشتررین چمرز را‬
‫فراهم مغپند‪ .‬به هممن دلمل‪ ،‬هممشه پماده سازی اولمه__‪ __repr‬را به پالسهای خود‬

‫‪1 DRY – Don’t Repeat Yourself‬‬


‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪123 ‬‬

‫اضافه مغپرنم‪ .‬در اینزرا مثرال پراملغ بررای پرایتون ‪ ،3‬از جملره پمراده سرازی اختمراری‬
‫__‪ __str‬وجود دارد‪.‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫‪def __repr__(self):‬‬
‫'(}__‪return (f'{self.__class__.__name‬‬
‫)')}‪f'{self.color!r}, {self.mileage!r‬‬

‫‪def __str__(self):‬‬
‫'‪return f'a {self.color} car‬‬

‫‪ .3-2-4‬تفاوتهای پایتون ‪__unicode__ :2.x‬‬


‫پایتون ‪ 2.x‬از مدل متفاوتغ برای تبدیل رشتهها استفاده مغپند‪ .‬دو راه بررای نمرایی‬
‫متن ‪ str‬په محدود بره مزموعره پاراپترهرای ‪ ASCII‬اسرت و ‪ unicode‬پره معرادل ‪str‬‬
‫پایتون ‪ 3‬است‪ ،‬وجود دارد‪.‬‬
‫به دلمل ایرن تفراوت‪ ،‬روش دانردر دیگرری بررای پنتررل تبردیل رشرته در پرایتون ‪:2‬‬
‫__‪ __unicode‬وجرررود دارد‪ .‬در پرررایتون ‪ __str__ ،2‬بایتهرررا را برمرررغگردانرررد و‬
‫__‪ __unicode‬پاراپترها را برمغگرداند‪.‬‬
‫برای بمشتر اهداف‪ __unicode__ ،‬روش جدیدتر و ترجمحغ بررای پنتررل تبردیل‬
‫رشته است‪ .‬تابع داخلغ )(‪ unicode‬نمز وجود دارد په مغتوانمد از آن استفاده پنمد‪ .‬این‬
‫پارشبمه به نحوه پار )(‪ str‬و )(‪ rep‬خواهد بود‪.‬‬
‫تا اینزا بسمار خوب برود‪ .‬حرال‪ ،‬زمرانغ پره بره قواعرد فراخروانغ در پرایتون ‪ 2‬بررای‬
‫__‪ __str‬و __‪ __unicode‬نگاه مغپنمد‪ ،‬پمغ رمرعادی به نظر مغرسد‪.‬‬
‫عبارت ‪ print‬و )(‪ str‬متد داندر __‪ __str‬را در صورت وجود فراخروانغ مرغپنرد‪.‬‬
‫متد داخلغ )(‪ unicode‬نمز متد دانردر __‪ __unicode‬را در صرورت وجرود فراخروانغ‬
‫مغپند‪ ،‬در رمر اینصورت متد داندر __‪ __str‬فراخوانغ خواهد شد‪.‬‬
‫‪  124‬ترفندهای پایتون‬

‫در مقایسه با پایتون ‪ ،3‬این موارد خا ‪ ،‬قواعد تبدیل متن را تا حدودی پمچمرده مرغ‬
‫پند‪ .‬اما راهغ برای ساده پردن چمزها برای اهداف پاربردی وجود دارد‪ .‬یونماد‪ ،‬شموه‬
‫ترجمحغ و آیندهآزمایغ برای دستااری متن در برنامههای پایتون شما است‪.‬‬
‫بنابراین‪ ،‬آنچه په توصمه مغپنم در پایتون ‪ 2.x‬انزام دهمد‪ ،‬این است پره تمرام پرد‬
‫فرمتدهغ رشته خود را داخل متد داندر __‪ __unicode‬قرار دهمد و بعد پماده سرازی‬
‫متد داندر __‪__str‬په نمایی یونماد رمزگذاری شده با عنوان ‪ UTF-8‬برمغگردانرد را‬
‫ایزاد پنمد‪.‬‬

‫‪def __str__(self):‬‬
‫)'‪return unicode(self).encode('utf-8‬‬

‫متد داندر __‪ __str‬برای بمشتر پرالسهرایغ پره مرغنویسرمد یاسران خواهرد برود‪،‬‬
‫بنابراین مغتوانمد در صورت لزوم آنرا پپغ و پمست پنمد‪ .‬تمرام پردهای تبردیل رشرته‬
‫په برای استفاده رمر توسعهدهنده در نظرر گرفتره شرده‪ ،‬در مترد دانردر __‪__unicode‬‬
‫قرار دارند‪.‬‬
‫در اینزا مثال پاملغ برای استفاده از این روش در پایتون ‪ 2.x‬ارائه شده است‪.‬‬

‫‪class Car(object):‬‬
‫‪def __init__(self, color, mileage):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬

‫‪def __repr__(self):‬‬
‫(‪return '{}({!r}, {!r})'.format‬‬
‫‪self.__class__.__name__,‬‬
‫)‪self.color, self.mileage‬‬

‫‪def __unicode__(self):‬‬
‫(‪return u'a {self.color} car'.format‬‬
‫)‪self=self‬‬

‫‪def __str__(self):‬‬
‫)'‪return unicode(self).encode('utf-8‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪125 ‬‬

‫‪ .4-2-4‬نکات کلیدی مقایسه __‪ __str‬و __‪__repr‬‬

‫‪ ‬با استفاده از متدهای داندر __‪ __str‬و __‪ __repr‬مغتوانمد تبدیل به رشتهها را‬
‫در پالسهای خود پنترل پنمد‪.‬‬
‫‪ ‬نتمزه متد داندر __‪ __str‬باید خوانا باشرد‪ .‬نتمزره مترد دانردر__‪ __repr‬بایرد‬
‫بدون ابهام باشد‪.‬‬
‫‪ ‬همواره متد داندر __‪ __repr‬را به پالسهای خرود اضرافه پنمرد‪ .‬پمرادهسرازی‬
‫پمیفرض __‪ __str‬فقط متد داندر __‪ __repr‬را فراخوانغ مغپند‪.‬‬
‫‪ ‬بزررای اسررتفاده از متررد __‪ __str‬در پررایتون ‪ 2‬از متررد __‪ __unicode‬اسررتفاده‬
‫پنمد‪.‬‬

‫‪ .3-4‬کالسهای ‪ Exception‬سفارشی‬
‫هنگامغ په از پایتون استفاده مغپردم‪ ،‬به نوشتن پالسهای استثناء سفارشغ در پرد‬
‫خود تردید داشتم‪ .‬اما تعریف انوا خطاها مغتواند بسمار ارزشمند باشد‪ .‬مغتوانمد موارد‬
‫خطای احتمالغ را به وضوح مشخص پنمد و در نتمزه توابع و ماژولهای شما بهتر قابرل‬
‫مدیریت خواهد بود‪ .‬همچنمن مغتوانمد از انوا خطاهای سفارشغ برای ارائره اطالعرات‬
‫بمشتری در مورد اشاالزدایغ استفاده پنمد‪.‬‬
‫پلمه این موارد پد پایتون شما را بهبود بخشمده و درک‪ ،‬اشاالزدایغ و نگهداری از‬
‫آن را راحتتر خواهد پرد‪ .‬تعریف پالسهای استثناء در هنگام تززیه آن به چند مثرال‬
‫ساده چندان سخت نمست‪ .‬در این فصل‪ ،‬ناات اصلغ په باید به خاطر بسرپارید‪ ،‬را بمران‬
‫خواهمم پرد‪.‬‬
‫فرض پنمد بخواهمد رشته ورودی یک تابع را په نشانگر نام یک شرخص در برنامره‬
‫شما است‪ ،‬را اعتبار سنزغ پنمد‪ .‬نمونه تابعغ برای اعتبارسنزغ نرام ممارن اسرت بردین‬
‫صورت باشد‪.‬‬
‫‪  126‬ترفندهای پایتون‬

‫‪def validate(name):‬‬
‫‪if len(name) < 10:‬‬
‫‪raise ValueError‬‬

‫در صورت عدم انزام اعتبارسنزغ‪ ،‬استثناء ‪ ValueError‬ایزاد مغشرود‪ .‬اپنرون ایرن‬
‫روش به نظر مناسب مغباشد و نوعغ سمنتاس پایتونمک است‪ .‬تا اینزا بسمار خوب بود‪.‬‬
‫با این حال‪ ،‬در استفاده از پالس استثنایغ عمومغ سطح باال مانند ‪ ValueError‬نقطره‬
‫ضعفغ وجود دارد‪ .‬تصور پنمد یاغ از هم تممغهای شما این تابع را بصورت بخشرغ از‬
‫پتابخانه فراخوانغ مغپند و اطالعات زیادی درباره بخیهای داخلغ آن نردارد‪ .‬وقترغ‬
‫در اعتبارسنزغ یک نام موفق نمغشود‪ ،‬در پنسول پمام زیر نمایی داده مغشود‪.‬‬

‫)'‪>>> validate('joe‬‬
‫‪Traceback (most recent call last):‬‬
‫>‪File "<input>", line 1, in <module‬‬
‫)'‪validate('joe‬‬
‫‪File "<input>", line 3, in validate‬‬
‫‪raise ValueError‬‬
‫‪ValueError‬‬

‫در واقع‪ ،‬این اطالعات چندان سودمند نمست‪ .‬قطعا‪ ،‬مغدانمم په یه چمزی اشتباه شده‬
‫و مسئله با "مقدار ناصحمح" روبهرو شده است‪ ،‬اما برای ایناه بتوانمد مسئله را بررای هرم‬
‫تممغ خود حل پنمد‪ ،‬مسلما باید به دنبال بررسرغ ترابع )(‪ validate‬باشرمد‪ .‬برا ایرن حرال‪،‬‬
‫خواندن پدها وقتگمر است اما این پار مغتواند به سرعت انزام شود‪.‬‬
‫خوشبختانه‪ ،‬مغتوانمم عملارد بهتری داشته باشمم‪ .‬اجازه دهمد نوعغ استثناء سفارشغ‬
‫را پمادهسازی پنمم تا اعتبارسنزغ ناموفق نام را نشان دهمم‪ .‬پالس استثناء جدید خرود را‬
‫از ‪ ValueError‬داخلغ پایتون ارث بری مغپنمم‪ ،‬اما با دادن نامغ صریحتر به آن‪ ،‬باعث‬
‫مغشویم خودش صحبت پند‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪127 ‬‬

‫‪class NameTooShortError(ValueError):‬‬
‫‪pass‬‬

‫‪def validate(name):‬‬
‫‪if len(name) < 10:‬‬
‫)‪raise NameTooShortError(name‬‬

‫حال‪ ،‬یک نو استثناء "خود استنادی" تحت عنروان ‪ NameTooShortError‬داریرم‬


‫پرره پررالس ‪ ValueError‬داخلرغ را گسررترش مررغدهررد‪ .‬در حالررت پلرغ‪ ،‬مررغخواهمررد‬
‫استثناءهای سفارشغ خود را از پالس استثناء ریشه یرا سرایر اسرتثناءهای داخلرغ پرایتون‬
‫مانند ‪ ValueError‬یا ‪ ،TypeError‬هرپدام په مناسب باشد را ارث بری پنمد‪.‬‬
‫همچنمن‪ ،‬ببمنمد په چگونه وقتغ آن را به عنوان نمونهای معتبر معرفغ مغپنمم‪ ،‬متغمرر‬
‫نام را به سازنده پالس استثناء سفارشغ خود منتقل مرغپنرمم؟ اجررای جدیرد منزرر بره‬
‫نمایی خطای بسمار بهتری برای هماارانتان مغشود‪.‬‬

‫)'‪>>> validate('jane‬‬
‫‪Traceback (most recent call last):‬‬
‫>‪File "<input>", line 1, in <module‬‬
‫)'‪validate('jane‬‬
‫‪File "<input>", line 3, in validate‬‬
‫)‪raise NameTooShortError(name‬‬
‫‪NameTooShortError: jane‬‬

‫یابار دیگر‪ ،‬خودتان را بزای همتممغ خود قرار دهمد‪ .‬این پار‪ ،‬درک پرالسهرای‬
‫استثناء سفارشغ را بسمار آسانتر مغپند‪ .‬مخصوصا هنگامغ په با مشالغ روبرو مغشویم‬
‫(په در نهایت با آن روبرو خواهمم شد) چه اتفاقغ باید رخ دهد‪.‬‬
‫این موضو باید در نظر گرفته شود حتغ اگر به تنهایغ روی پد منبع پار مرغپنمرد‪.‬‬
‫چند هفته یا چند ماه بعرد‪ ،‬در صرورت داشرتن سراختار مناسرب‪ ،‬زمران نگهرداری بسرمار‬
‫پمتری برای پدتان صرف خواهمد پرد‪.‬‬
‫‪  128‬ترفندهای پایتون‬

‫با صرف فقط ‪ 30‬انمه برای تعریف پالس استثناء ساده‪ ،‬این تاره پرد حراال بره پرد‬
‫بسمار مفمدتری تبدیل مغشود‪ .‬اما اجازه دهمد تا ادامه دهمم‪ .‬موارد بمشرتری بررای مطررح‬
‫پردن وجود دارند‪.‬‬
‫زمانغ په پامج پایتونغ را به صورت عمومغ منتشر مرغپنمرد‪ ،‬یرا حترغ مراژول قابرل‬
‫استفاده مزدد برای شرپت خود ایزاد مغپنمد‪ ،‬شرموه خروبغ بررای ایزراد پرالس پایره‬
‫استثناء سفارشغ برای ماژول (پامج) است و بعرد سرایر اسرتثناءهای خرود را برر پایره آن‬
‫مغسازید‪.‬‬
‫در اینزا نحوه ایزاد سلسله مراتب استثناء سفارشغ برای همه اسرتثناءها در مراژول یرا‬
‫پامجهای پایتون ارائه شده است‪ .‬اولمن گام‪ ،‬اعالن پالس پایه است پره همره خطاهرای‬
‫برنامهی ما از آن ارث بری خواهند پرد‪.‬‬

‫‪class BaseValidationError(ValueError):‬‬
‫‪pass‬‬

‫حال‪ ،‬پلمه پالسهای خطای "واقعغ" خود را مغتوانمد از پالس خطای پایه بدست‬
‫آورید‪ .‬این پار با پمغ تالش بمشتر‪ ،‬سلسله مراتب استثنای خوب و تممزی را ارائره مرغ‬
‫دهد‪.‬‬

‫‪class NameTooShortError(BaseValidationError):‬‬
‫‪pass‬‬

‫‪class NameTooLongError(BaseValidationError):‬‬
‫‪pass‬‬

‫‪class NameTooCuteError(BaseValidationError):‬‬
‫‪pass‬‬

‫برای مثال‪ ،‬این به پاربران پامج شما اماان نوشتن عبارت ‪ try…except‬را مغدهد تا‬
‫بتوانند تمام خطاهای ایزاد شده توسط پامج پایتونغ شما را پنترل پنند‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪129 ‬‬

‫‪try:‬‬
‫)‪validate(name‬‬
‫‪except BaseValidationError as err:‬‬
‫)‪handle_validation_error(err‬‬

‫پاربرانتان مغتوانند بدین شموه اسرتثناءهای خرا ترری را بدسرت آورنرد‪ ،‬امرا اگرر‬
‫نخواهند‪ ،‬حداقل مزبور نخواهند بود برای گرفتن استثناءها با قالب در پد به دنبال خطرا‬
‫بگردند‪ .‬البته این پار ممان است گاهغ یک ضدالگو در نظر گرفته شود زیرا مغتوانرد‬
‫خطاهای نامربوط برنامه را در بلند مدت از بمن ببرد و پنهان پند و اشراالزدایغ برنامره‬
‫های شما را بسمار سختتر پند‪.‬‬
‫البته‪ ،‬مغتوانمد این ایده را بمشتر گسترش دهمد و به لحاظ منطقرغ اسرتثنائات خرود را‬
‫در سلسله مراتب فرعغ گروهبندی پنمد‪ .‬اما مراقب باشمد‪ ،‬زیادهروی پردن در این پار‪،‬‬
‫ورود پمچمدگغ رمرضروری به پدهای شما را آسان مغپند‪.‬‬

‫‪ .1-3-4‬نکات کلیدی استثناهای سفارشی‬


‫‪ ‬با تعریف انوا استثناءهای اختصاصغ‪ ،‬ماهمت پد شرما برا وضروح بمشرتری بمران‬
‫خواهد شد و اشاالزدایغ پدها راحتتر خواهد بود‪.‬‬
‫‪ ‬استثناءهای سفارشغ خرود را از پرالس اسرتثنای داخلرغ پرایتون ‪ Exception‬یرا‬
‫پالسهای استثنای اختصاصغ مانند ‪ ValueError‬یا ‪ KeyError‬تولمد پنمد‪.‬‬
‫‪ ‬مغتوانمد از ورا ت برای تعریف سلسرله مراترب اسرتثناءهای دارای گرروهبنردی‬
‫منطقغ استفاده پنمد‪.‬‬
‫‪  130‬ترفندهای پایتون‬

‫‪ Clone .4-4‬اشیاء برای تفریح و سود‬


‫هنگام ساخت یک نمونه از یک شغء در پایتون‪ ،‬پپغهایغ از اشماء ایزاد نمغشرود‪.‬‬
‫داده مغشرود‪ .‬در مرورد اشرماء تغممرناپرذیر‪،‬‬ ‫بلاه تنها نام جدیدی به یک شغء اختصا‬
‫معموال تغممری ایزاد نمغشود‪.‬‬
‫اما برای پار با اشماء تغممرپذیر یا مزموعههایغ از اشرماء تغممرپرذیر‪ ،‬ممارن اسرت بره‬
‫دنبال روشغ برای ایزاد "پپغهای واقعغ" یا "پلونهایغ" از این اشماء باشمد‪.‬‬
‫اساسا‪ ،‬گاهغ پپغهرایغ از اشرماء را مرغخواهمرد ترا بتوانمرد بردون انزرام اصرالحات‬
‫خودپرار در شرغء اصررلغ‪ ،‬اصرالحات الزم را انزررام دهمرد‪ .‬در ایرن فصررل‪ ،‬مرغخررواهم‬
‫خالصهای در مورد نحوه پپغ یا "‪ "clone‬اشماء در پایتون و برخغ هشردارهای مربروط‬
‫به آن صحبت پنم‪.‬‬
‫اجازه دهمد با بررسغ نحوه پپغ پردن مزموعرههرای داخلرغ‪1‬پرایتون شررو پنرمم‪.‬‬
‫مزموعههای تغممرپذیر داخلغ پایتون مانند لمستها‪ ،‬دیاشنریها و ‪ set‬ها را مغتروان برا‬
‫فراخوانغ توابع آنها روی مزموعه موجود پپغ پرد‪.‬‬

‫)‪new_list = list(original_list‬‬
‫)‪new_dict = dict(original_dict‬‬
‫)‪new_set = set(original_set‬‬

‫با این حال‪ ،‬این روش برای اشماء سفارشغ مناسب نمست و از همه مهمتر ایناره‪ ،‬ایرن‬
‫روش فقط پپغهای سطحغ‪ 2‬را ایزاد مغپند‪ .‬برای مزموعههای تغممرپذیر مانند لمسرت‬
‫ها‪ ،‬دیاشنریها و ‪set‬ها‪ ،‬یک تفاوت مهم بمن پپغ سطحغ و پپغ عممق‪ 3‬وجود دارد‪.‬‬
‫پپغ سطحغ به معنای ایزاد شغء جدید مزموعه و بعد مقداردهغ آن با مراجعره بره‬
‫اشماء موجود در لمست اصلغ است‪ .‬در حقمقت‪ ،‬پپغ سطحغ فقط یک عمق دارد‪ .‬روند‬

‫‪1 Built-in collections‬‬


‫‪2 Shallow copy‬‬
‫‪3 Deep copy‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪131 ‬‬

‫پپغبرداری تارار نمغشود و از ایرن رو‪ ،‬پپرغ جدیردی از اشرماء درون مزموعره پپرغ‬
‫شده‪ ،‬ساخته نخواهد شد‪.‬‬
‫پپغ عممق‪ ،‬روند پپغ را بصورت برگشتغ ‪1‬انزام مغدهد‪ .‬این بدین معنغ اسرت پره‬
‫ابتدا شغء مزموعه جدید ساخته مغشود و بعد بصورت برگشرتغ برا پپرغ اشرماء فرزنرد‬
‫موجود در مزموعه اصلغ مقداردهغ مغشود‪ .‬پپغبرداری از یرک شرغء برا ایرن روش‪،‬‬
‫پل درخت شغ ‪2‬را طغ مغپند تا پلون پامالد مستقل از شغء اصلغ و همه فرزنردان آن‬
‫ایزاد پند‪.‬‬
‫مغدانم‪ ،‬توضمحات پمغ طوالنغ بود‪ .‬پس اجازه دهمد چند مثال را بررسغ پنرمم ترا‬
‫این تفاوت بمن پپغهای سطحغ و عممق را مشخص پنمم‪.‬‬

‫‪ .1-4-4‬ایجاد کپیهای سطحی‬


‫در مثال زیر‪ ،‬لمست تو در توی جدیدی ایزاد خواهمم پرد و بعد به صورت سرطحغ‬
‫آن را با تابع )(‪ list‬پپغ مغپنمم‪.‬‬

‫]]‪>>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬


‫‪>>> ys = list(xs) # Make a shallow copy‬‬

‫این بدان معناست په ‪ ys‬حاال یک شغء جدیرد و مسرتقل برا محتویرات مشرابه برا ‪xs‬‬
‫خواهد بود‪ .‬با بررسغ هر دو شغء مغتوانمد این موضو را تایمد پنمد‪.‬‬

‫‪>>> xs‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬
‫‪>>> ys‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬

‫برای تایمد ایناه ‪ ys‬واقعا مستقل از لمست اصلغ است‪ ،‬اجازه دهمد آزمایی پوچاغ‬
‫را انزام دهمم‪ .‬شما مغتوانمد زیرلمست جدیدی را امتحان پرده و آن را به لمست اصرلغ‬

‫‪1 recursive‬‬
‫‪2 Object tree‬‬
‫‪  132‬ترفندهای پایتون‬

‫(‪ )xs‬اضافه پنمد و سپس بررسغ پنمد تا مطمئن شوید این اصرالحات برر پپرغ (‪ )ys‬ا رر‬
‫نمغگذارد‪.‬‬

‫)]'‪>>> xs.append(['new sublist‬‬


‫‪>>> xs‬‬
‫]]'‪[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist‬‬
‫‪>>> ys‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬

‫همانطور په مغبمنمد‪ ،‬این پار ا ر مورد انتظار را داشت‪ .‬اصالح لمست پپرغ شرده در‬
‫سطح "ظاهری" اصال مشال نبود‪.‬‬
‫با این حال‪ ،‬چون فقط پپغ سطحغ لمست اصرلغ را ایزراد پرردیم‪ ys ،‬هنروز حراوی‬
‫ارجا هایغ به اشماء فرزند لمست اصلغ ذخمره شده در ‪ xs‬است‪ .‬این فرزندان به صرورت‬
‫پامل پپغ نشده بودند‪.‬‬
‫بنابراین‪ ،‬هنگامغ په یارغ از اشرماء فرزنرد را در ‪ xs‬اصرالح مرغپنمرد‪ ،‬بازتراب ایرن‬
‫اصالحات در ‪ ys‬نمز خواهد بود! این اتفاق به این دلمرل اسرت پره هرر دو لمسرت‪ ،‬اشرماء‬
‫فرزند مشابهغ را به اشتراک مغگذارند‪ .‬این پپغ فقط یک پپغ سطحغ اسرت‪ ،‬درواقرع‬
‫در مرحله پپغ پردن‪ ،‬تنها یک عمق پپغ اعمال مغشود‪.‬‬

‫'‪>>> xs[1][0] = 'X‬‬


‫‪>>> xs‬‬
‫]]'‪[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist‬‬
‫‪>>> ys‬‬
‫]]‪[[1, 2, 3], ['X', 5, 6], [7, 8, 9‬‬

‫در مثال فوق (بره ظراهر) فقرط ‪ xs‬را تغممرر دادهایرم‪ .‬امرا معلروم مرغشرود پره هرر دو‬
‫زیرلمست با اندیس ‪ 1‬در ‪ xs‬و ‪ ys‬اصالح شدند‪ .‬از طرف دیگر‪ ،‬سبب وقو این امرر ایرن‬
‫بود په فقط پپغ سطحغ لمست اصلغ را ایزاد پرده بودیم‪.‬‬
‫اگر در وهله اول پپغ عممق از ‪ xs‬ایزراد پررده برودیم‪ ،‬هرر دو شرغء پرامالد مسرتقل‬
‫بودند‪ .‬این تفاوت پاربردی بمن پپغهای سطحغ و عممق اشماء است‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪133 ‬‬

‫حال مغدانمد په چگونه پپغهای سطحغ برخغ پالسهای مزموعه داخلغ پرایتون‬
‫را ایزاد پنمد و تفاوت بمن پپغبرداری سطحغ و عممق را نمز مغدانمد‪ .‬سؤاالتغ په حاال‬
‫مغخواهمم به آنها پاس دهمم عبارتند از‪:‬‬
‫‪ ‬چگونه مغتوانمد پپغهای عممق را در مزموعههای داخلغ پایتون ایزاد پنمد؟‬
‫‪ ‬چگونه مغتوانمد پپغهای سطحغ و عممق اشماء دلخرواه‪ ،‬از جملره پرالسهرای‬
‫سفارشغ را ایزاد پنمد؟‬
‫پاس این سؤاالت در ماژول پپغ در پتابخانه استاندارد پایتون قرار دارد‪ .‬این ماژول‬
‫رابط سادهای را برای ایزاد پپغهای سطحغ و عممق اشماء دلخواه پایتون فراهم مغپند‪.‬‬

‫‪ .2-4-4‬ایجاد کپیهای عمیق‬


‫اجازه دهمد مثال قبلغ در مورد پپغبرداری از لمست را‪ ،‬با یرک تفراوت مهرم تاررار‬
‫پنمم‪ .‬این بار مرغخرواهمم از ترابع )(‪ deepcopy‬تعریرف شرده در مراژول ‪ copy‬پرایتون‬
‫استفاده پنمم و یک نسخه پپغ عممق ایزاد پنمم‪.‬‬

‫‪>>> import copy‬‬


‫]]‪>>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬
‫)‪>>> zs = copy.deepcopy(xs‬‬

‫وقتغ ‪ xs‬و پلون ‪ zs‬آن په با )(‪ copy.deepcopy‬ایزاد شد را بررسغ پنمد‪ ،‬مشاهده‬


‫خواهمد پرد په هر دوی آنها دوباره یاسان به نظر مغرسند‪.‬‬

‫‪>>> xs‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬
‫‪>>> zs‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬

‫با این حال‪ ،‬اگر در یاغ از اشماء فرزند در شغ اصلغ (‪ )xs‬اصرالحاتغ انزرام دهمرد‪،‬‬
‫خواهمد دید په این اصالحات روی پپغ عممق (‪ )zs‬ا ری نخواهد داشت‪.‬‬
‫‪  134‬ترفندهای پایتون‬

‫این بار هر دو شرغء‪ ،‬اصرل و پپرغ‪ ،‬پرامالد مسرتقل از یاردیگر هسرتند‪ .‬پپرغ ‪ xs‬بره‬
‫صورت برگشتغ پلونسازی شد‪ ،‬په شامل همه اشماء فرزند آن مغباشد‪.‬‬

‫'‪>>> xs[1][0] = 'X‬‬


‫‪>>> xs‬‬
‫]]‪[[1, 2, 3], ['X', 5, 6], [7, 8, 9‬‬
‫‪>>> zs‬‬
‫]]‪[[1, 2, 3], [4, 5, 6], [7, 8, 9‬‬

‫ممان است بخواهمد مدتغ با مفسر پایتون بنشمنمد و این مثالها را اجررا پنمرد‪ .‬وقترغ‬
‫مستقمما مثالها را اجرا مغپنمد‪ ،‬فار پردن در مورد اشماء پپغبرداری شرده راحرت ترر‬
‫است‪.‬‬
‫در هر حال‪ ،‬مغتوانمد با استفاده از یک تابع دیگر در ماژول پپغ‪ ،‬پپغهرای سرطحغ‬
‫نمز ایزاد پنمد‪ .‬تابع )(‪ copy.copy‬پپغهای سطحغ اشماء را ایزاد مغپند‪.‬‬
‫اگر الزم است ارتباط واضحغ بمن اشماء برقرار پنمرد و در جرایغ از پرد خرود یرک‬
‫پپغ سطحغ ایزاد پنمد‪ ،‬این پار مفمد خواهد برود‪ .‬اسرتفاده از ترابع )(‪ copy.copy‬ایرن‬
‫اماان را به شما مغدهد تا این واقعمت را درک پنمد‪ .‬برای مزموعرههرای داخلرغ‪ ،‬ایرن‬
‫پار بمشتر پایتونمک به نظر مغرسد تا صرفا از توابع ‪ dict ،list‬و ‪ set‬پایتون بررای ایزراد‬
‫پپغهای سطحغ استفاده پنمد‪.‬‬

‫‪ .3-4-4‬کپیبرداری از اشیاء دلخواه‬


‫سؤالغ په باید به آن پاس دهمم این است په چگونه مغتوانمم پپغهرای سرطحغ و‬
‫عممق اشماء دلخواه‪ ،‬از جمله پالسهای سفارشغ را ایزاد پنمم‪.‬‬
‫حال به بررسغ آن مغپردازیم‪.‬‬
‫درحقمقررت دوبرراره مرراژول پپررغ برره پمررک مررا مررغآیررد‪ .‬توابررع )(‪ copy.copy‬و‬
‫)(‪ copy.deepcopy‬را مغتوان برای تاثمر هر شغ باار برد‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪135 ‬‬

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

‫‪class Point:‬‬
‫‪def __init__(self, x, y):‬‬
‫‪self.x = x‬‬
‫‪self.y = y‬‬

‫‪def __repr__(self):‬‬
‫')}‪return f'Point({self.x!r}, {self.y!r‬‬

‫اممدوارم موافق باشمد په ایرن موضرو نسربتا سراده برود‪ .‬مترد دانردر )(__‪ __repr‬را‬
‫اضافه پردم تا بتوانمم به راحتغ اشماء ایزاد شده از این پالس را در مفسر پایتون بررسغ‬
‫پنمم‪.‬‬
‫در مرحله بعدی‪ ،‬با استفاده از ماژول پپغ نمونه ای از شغء ‪ Point‬ایزاد مرغپنرمم و‬
‫بعد (بصورت سطحغ) آن را پپغ مغپنمم‪.‬‬

‫)‪>>> a = Point(23, 42‬‬


‫)‪>>> b = copy.copy(a‬‬

‫اگر محتویات شغء ‪ Point‬اصلغ و پپغ سطحغ آن را بررسرغ پنرمم‪ ،‬نتمزره زیرر را‬
‫خواهمم دید‪.‬‬

‫‪>>> a‬‬
‫)‪Point(23, 42‬‬
‫‪>>> b‬‬
‫)‪Point(23, 42‬‬
‫‪>>> a is b‬‬
‫‪False‬‬
‫‪  136‬ترفندهای پایتون‬

‫این مورد دیگری است په باید آن را در ذهن نگه داریرد‪ .‬چرون شرغء ‪ Point‬بررای‬
‫مختصات خود از نو تغممرناپذیر داده (‪ )int‬استفاده مغپند‪ ،‬پس همک تفاوتغ برمن پپرغ‬
‫سطحغ و عممق در این مورد وجود ندارد‪.‬‬
‫اجازه دهمد مثال پمچمدهتری را در نظر بگمریم‪ .‬مغخرواهم پرالس دیگرری را بررای‬
‫نمایی مستطملهای ‪ 2‬بعدی تعریف پنم‪ .‬این پار را به روشغ انزام خواهم داد په به ما‬
‫اماان ایزاد سلسله مراتب شغء پمچمده را بدهد‪ .‬مستطملهای من از اشماء نقطرهای بررای‬
‫نمایی مختصات خود استفاده خواهند پرد‪.‬‬

‫‪class Rectangle:‬‬
‫‪def __init__(self, topleft, bottomright):‬‬
‫‪self.topleft = topleft‬‬
‫‪self.bottomright = bottomright‬‬

‫‪def __repr__(self):‬‬
‫' ‪return (f'Rectangle({self.topleft!r},‬‬
‫)')}‪f'{self.bottomright!r‬‬

‫از طرف دیگر‪ ،‬ابتدا مغخواهمم پپغ سطحغ نمونه مستطمل را ایزاد پنمم‪.‬‬

‫))‪rect = Rectangle(Point(0, 1), Point(5, 6‬‬


‫)‪srect = copy.copy(rect‬‬

‫در صورت بررسغ مستطمل اصلغ و پپغ آن‪ ،‬خواهمد دید په نادیده گررفتن ترابع ()‬
‫__‪ __ repr‬چقدر مو ر است و ایناه روند پپغ سطحغ طبق انتظار ما پار مغپند‪.‬‬

‫‪>>> rect‬‬
‫))‪Rectangle(Point(0, 1), Point(5, 6‬‬
‫‪>>> srect‬‬
‫))‪Rectangle(Point(0, 1), Point(5, 6‬‬
‫‪>>> rect is srect‬‬
‫‪False‬‬

‫به یاد داشته باشمد په چگونه نمونه قبلغ لمست تفاوت بمن پپغهای عممق و سرطحغ‬
‫را نشان داد؟ در اینزا مغخواهم از رویاردی مشابه استفاده پنم‪ .‬یک شغء را از قسمت‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪137 ‬‬

‫پایمن در سلسلهمراتب شغء اصرالح خرواهم پررد و بعرد بازتراب ایرن تغممرر را در پپرغ‬
‫(سطحغ) نمز مشاهده خواهمد پرد‪.‬‬

‫‪>>> rect.topleft.x = 999‬‬


‫‪>>> rect‬‬
‫))‪Rectangle(Point(999, 1), Point(5, 6‬‬
‫‪>>> srect‬‬
‫))‪Rectangle(Point(999, 1), Point(5, 6‬‬

‫اممدوارم این توابع طوری په انتظار آن را داشتمد‪ ،‬رفتار پنند‪ .‬در مرحلره بعرد‪ ،‬پپرغ‬
‫عممق مستطمل اصلغ را ایزاد خواهمم پرد‪ .‬پرس اصرالحات دیگرری را اعمرال پررده و‬
‫خواهمد دید په پدام اشماء تحت تأ مر قرار مغگمرند‪.‬‬

‫)‪>>> drect = copy.deepcopy(srect‬‬


‫‪>>> drect.topleft.x = 222‬‬
‫‪>>> drect‬‬
‫))‪Rectangle(Point(222, 1), Point(5, 6‬‬
‫‪>>> rect‬‬
‫))‪Rectangle(Point(999, 1), Point(5, 6‬‬
‫‪>>> srect‬‬
‫))‪Rectangle(Point(999, 1), Point(5, 6‬‬

‫بفرمائمد! این بار پپرغ عممرق (‪ )drect‬پرامالد مسرتقل از پپرغ اصرلغ (‪ )rect‬و پپرغ‬
‫سطحغ (‪ )srect‬است‪.‬‬
‫در اینزا قسمت زیادی از مبحث را پوشی دادهایم‪ ،‬اما هنوز ناتههای ریزتری بررای‬
‫پپغبرداری از اشماء وجود دارند‪.‬‬
‫برای پرداخت بمشتر این موضو ‪ ،‬ممان اسرت بخواهمرد در مرورد مسرتندات مراژول‬
‫پپغ و احتماال پد منبع ماژول بمشتر مطالعه پنمد‪ .‬برای مثال‪ ،‬اشماء مغتواننرد برا تعریرف‬
‫)(__‪ __copy‬و )(__‪ __deepcopy‬روی آنها‪ ،‬نحوه پپغبرداری را پنترل‬ ‫توابع خا‬
‫پنند‪.‬‬
‫‪  138‬ترفندهای پایتون‬

‫‪ .4-4-4‬نکات کلیدی ‪ Clone‬اشیاء در پایتون‬


‫‪ ‬ایزاد یک پپغ سطحغ از یک شغء‪ ،‬اشماء فرزند را پلونغ یا تاثمرر نمرغپنرد‪.‬‬
‫بنابراین‪ ،‬این پپغ پامالد مستقل از پپغ اصلغ نمست‪.‬‬
‫‪ ‬پپغ عممق شغء بصورت برگشتغ اشرماء فرزنرد را پلرونغ خواهرد پررد‪ .‬پلرون‬
‫پامالد مستقل از پپغ اصلغ است‪ ،‬اما ایزاد پپغ عممق آهستهتر است‪.‬‬
‫‪ ‬مغتوانمد اشماء دلخواه (از جمله پالسهای سفارشغ) را برا مراژول ‪ ،copy‬پپرغ‬
‫برداری پنمد‪.‬‬

‫پایه‪1‬‬ ‫‪ .5-4‬بررسی وراثت در کالسهای انتزاعی‬


‫پالسهای انتزاعغ پایه (‪ABC‬ها) برای اطممنان حاصل پردن از پماده سازی متردهای‬
‫خاصغ از پالس پایه هستند‪ .‬در این فصل در مورد مزایرای پالسهرای انتزاعرغ پایره و‬
‫نحوه تعریف آنها با ماژول داخلغ ‪ abc‬پایتون آموزش داده خواهد شد‪.‬‬
‫پالسهای انتزاعغ پایه برای چه مرواردی مناسرب هسرتند؟ چنرد وقرت پرمی بحثرغ‬
‫درباره ایناه از پدام الگو برای پمراده سرازی سلسرله مراترب پرالس پایردار ‪2‬در پرایتون‬
‫استفاده پنم‪ ،‬داشتم‪ .‬هدف‪ ،‬تعریف سلسله مراتبغ از یک پرالس سراده بررای برک انرد‬
‫سرویس‪ ،‬یک روش پایدار و مطلوب برای برنامهنویسان بود‪.‬‬
‫ما یک پالس ‪ BaseService‬داشتمم په یرک ‪ interface‬مشرترک و چنردین پمراده‬
‫سازی معمول (رمرانتزاعغ) را تعریف مغپرد‪ .‬پمادهسازی های معمول پارهای مختلفرغ‬
‫را انزررام مررغدادنررد امررا همرره آنهررا رابررط پرراربری یاسررانغ داشررتند (‪،MockService‬‬
‫‪ ،RealService‬و رمره)‪ .‬برای ایزاد یک رابطه صریح برمن پالسهرا‪ ،‬همره پالسهرا از‬
‫پالس ‪ BaseService‬ارث برده شدند‪.‬‬

‫‪1 Abstract Base Classes‬‬


‫‪2 maintainable‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪139 ‬‬

‫برای ایناه این پد تا حد اماان پایدار و مورد پسند برنامهنویسان باشد‪ ،‬بایرد مطمرئن‬
‫شویم په‪:‬‬
‫‪ ‬نمونهسازی پالس پایه ناممان باشد‪.‬‬
‫‪ ‬فرامرروش پررردن پمررادهسررازی مترردهای ‪ interface‬در یاررغ از زیرررپالسهررا در‬
‫سریعترین زمان ممان خطایغ را آشاار پند‪.‬‬
‫حال‪ ،‬چرا مغخرواهمم از مراژول ‪ abc‬پرایتون بررای حرل ایرن مسرئله اسرتفاده پنرمم؟‬
‫طراحغ فوق در سمستمهای پمچمده نسبتا شایع است‪ .‬برای اجبار بره پمادهسرازی متردهای‬
‫پالس پایه در زیرپالسها‪ ،‬معموال از روش پایتونماغ بدین صورت استفاده مغشود‪.‬‬

‫‪class Base:‬‬
‫‪def foo(self):‬‬
‫)(‪raise NotImplementedError‬‬

‫‪def bar(self):‬‬
‫)(‪raise NotImplementedError‬‬

‫‪class Concrete(Base):‬‬
‫‪def foo(self):‬‬
‫'‪return 'foo() called‬‬

‫‪# Oh no, we forgot to override bar()...‬‬


‫‪# def bar(self):‬‬
‫‪#‬‬ ‫"‪return "bar() called‬‬

‫بنابراین‪ ،‬از اقدامات اولمره خرود بررای حرل ایرن مسرئله بره چره نتمزرهای مرغرسرمم؟‬
‫فراخوانغ متدهای پالس پایه به درستغ خطایغ را تولمد مغپند‪.‬‬

‫)(‪>>> b = Base‬‬
‫)(‪>>> b.foo‬‬
‫‪NotImplementedError‬‬
‫‪  140‬ترفندهای پایتون‬

‫افزون بر این‪ ،‬نمونرهسرازی از پرالس ‪ Concrete‬طبرق انتظرار پرمی مرغرود و اگرر‬


‫متدهای الزم پمادهسازی نشده باشند مانند )(‪ bar‬هنگام فراخوانغ یک استثنا (خطا) تولمد‬
‫مغشود‪.‬‬

‫)(‪>>> c = Concrete‬‬
‫)(‪>>> c.foo‬‬
‫'‪'foo() called‬‬
‫)(‪>>> c.bar‬‬
‫‪NotImplementedError‬‬

‫این پماده سازی اولمه مناسب است‪ ،‬اما هنوز پامل نمست‪ .‬در آن نقاط ضرعفغ وجرود‬
‫دارند ولغ مغتوانمم‪:‬‬
‫‪ ‬پالس پایه را بدون ایزاد خطا بدرستغ نمونهسازی پنمم‪.‬‬
‫‪ ‬زیرپالسهررای رمرپرراملغ را پمادهسررازی پن رمم و تررا زمررانغ پرره متررد )(‪ bar‬را‬
‫فراخوانغ نانمم‪ ،‬نمونهسازی معمول همک خطایغ ایزاد نخواهد پرد‪.‬‬
‫با ماژول ‪ abc‬پایتون افزوده شرده در پرایتون ‪ ،2.6‬مرغتروانمم عملاررد بهترری داشرته‬
‫باشمم و بقمه مسائل را حل پنمم‪ .‬در پماده سازی زیر‪ ،‬روش پماده سازی را ارتقا مرغدهمم‬
‫و از پالسهای انتزاعغ پایه استفاده خواهمم پرد‪.‬‬

‫‪from abc import ABCMeta, abstractmethod‬‬

‫‪class Base(metaclass=ABCMeta):‬‬
‫‪@abstractmethod‬‬
‫‪def foo(self):‬‬
‫‪pass‬‬

‫‪@abstractmethod‬‬
‫‪def bar(self):‬‬
‫‪pass‬‬

‫‪class Concrete(Base):‬‬
‫‪def foo(self):‬‬
‫‪pass‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪141 ‬‬

‫‪# We forget to declare bar() again...‬‬

‫این پار هنوز طبق پمیبمنغ رفتار مغپند و سلسلهمراتب پالس را به صورت صحمح‬
‫ایزاد مغپند‪.‬‬

‫)‪assert issubclass(Concrete, Base‬‬

‫با این حال‪ ،‬در این پد مزیرت دیگرری را مشراهده مرغپنمم‪ .‬هرر وقرت پمرادهسرازی‬
‫متدهای انتزاعغ را فرامروش پنرمم‪ ،‬زیرپالسهرای پایره اسرتثنا ‪ TypeError‬را در زمران‬
‫نمونهسازی ایزاد مغپند‪ .‬استثناء مطرح شده به ما مرغگویرد پردام مترد یرا متردها را از‬
‫دست دادهایم‪.‬‬

‫)(‪>>> c = Concrete‬‬
‫‪TypeError:‬‬
‫\ " ‪"Can't instantiate abstract class Concrete‬‬
‫"‪"with abstract methods bar‬‬

‫برررردون ‪ ،abc‬در صررررورت فراخرررروانغ متررررد از دسررررت رفترررره‪ ،‬فقررررط خطررررای‬


‫‪ NotImplementedError‬را دریافت مغپنمم‪ .‬پسرب اطالعرات در مرورد متردهای از‬
‫دست رفته در زمان نمونهسازی مزیرت بزرگرغ اسرت‪ .‬ایرن پرار نوشرتن زیرپالسهرای‬
‫نامعتبر را دشوارتر مغپند‪ .‬اگر در حال نوشتن پد جدید هستمد‪ ،‬ممان اسرت ایرن پرار‬
‫چندان مهمغ نباشد‪ ،‬اما قول مغدهم چند هفته یا چند ماه بعد‪ ،‬به نفعتان خواهد بود‪.‬‬
‫دریافتمم په این پار ارلب اوقات باعث مغشود سلسله مراتب پالسها پایدارتر و با‬
‫اطممنان باالتری قابل تغممر باشند‪ .‬استفاده از ‪ ABC‬به وضوح هردف برنامرهنرویس را بمران‬
‫مغپند و پد ارتباطغ را ایزاد مغپند‪ .‬به شما توصمه مغپنم تا مستندات مراژول ‪ abc‬را‬
‫بخوانمد و به وضعمتهایغ توجه پنمد په اعمال این الگو در آنها منطقغ است‪.‬‬
‫‪  142‬ترفندهای پایتون‬

‫‪ .1-5-4‬نکات اصلی کالسهای انتزاعی پایه‬


‫‪ ‬پالسهای انتزاعغ پایه (‪ )ABC‬تصریح مغپنند پره پالسهرای بدسرت آمرده‪،‬‬
‫متدهای خاصغ را از پالس پایه در زمان نمونهسازی پمادهسازی مغپنند‪.‬‬
‫‪ ‬استفاده از ‪ ABC‬به جلوگمری از اشااالت پمک مغپند و حفظ سلسرله مراترب‬
‫پالس را آسانتر مغپند‪.‬‬

‫‪ .6-4‬آشنایی با ‪namedtuple‬ها‬
‫پایتون با نو اختصاصغ پانتمنر "‪ "namedtuple‬همراه است په بره نظرر نمرغرسرد‬
‫چندان سزاوار تامل باشد‪ .‬این یاغ از آن ویژگغهای شگفتانگمز در پایتون است په با‬
‫دید باز نمز قابل مشاهده نمست‪.‬‬
‫استفاده از ‪namedtuple‬ها مغتوانرد جرایگزین بسرمار خروبغ بررای تعریرف دسرتغ‬
‫پالس باشد و ویژگغهای جالب دیگری نمز دارند په در این قسمت قصد دارم شرما را‬
‫با آنها آشنا پنم‪.‬‬
‫حال‪ namedtuple ،‬چمست و چه چمزی آن را خا ترر مرغپنرد؟ روشرغ مناسرب‬
‫برای فار پردن در مورد ‪namedtuple‬هرا‪ ،‬مشراهده آنهرا بصرورت افزونرهای از نرو‬
‫دادههای ‪ tuple‬است‪.‬‬
‫‪Tuple‬ها در پایتون‪ ،‬ساختار داده سرادهای بررای گرروهبنردی اشرماء دلخرواه هسرتند‪.‬‬
‫‪tuple‬ها تغممرناپذیر هستند‪ .‬یعنغ پس از ایزاد‪ ،‬آنها را نمغتوان اصالح پررد‪ .‬در ادامره‬
‫مثالغ مختصر در این زممنه وجود دارد‪.‬‬

‫)‪>>> tup = ('hello', object(), 42‬‬


‫‪>>> tup‬‬
‫)‪('hello', <object object at 0x105e76b70>, 42‬‬
‫]‪>>> tup[2‬‬
‫‪42‬‬
‫‪>>> tup[2] = 23‬‬
‫‪TypeError:‬‬
‫"‪"'tuple' object does not support item assignment‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪143 ‬‬

‫یک ناته مهم در مورد ‪tuple‬های ساده این است په دادههایغ په در آنهرا ذخمرره‬
‫مغپنمد را فقط با استفاده از اندیسهای عدد صحمح استخراج پرد‪ .‬نمغتوانمد به مقادیر‬
‫دهمد‪ .‬این مغتواند بر خوانایغ پد ا ر بگذارد‪.‬‬ ‫ذخمره شده در ‪ tuple‬نامهایغ اختصا‬
‫همچنمن‪ tuple ،‬همواره از ساختار موقتغ برخوردار است‪ .‬پسب اطممنان از ایناه دو‬
‫تاپل از تعداد فملدهای یاسران و خصوصرمات ذخمرره شرده مشرابه برخوردارنرد‪ ،‬دشروار‬
‫است‪ .‬این پار ممان است باعث مشاالتغ در هنگام پار با ‪ tuple‬ها شود‪.‬‬

‫‪ .1-6-4‬استفاده از ‪ namedtuple‬ها برای نجات‬


‫هدف ‪namedtuple‬ها حل این مشال است‪.‬‬
‫اول از همه‪namedtuple ،‬ها دقمقا مانند تاپلهرای معمرولغ پانتمنرهرای تغممرناپرذیر‬
‫هستند‪ .‬هنگام ذخمرهسازی دادههای سطح باال روی ‪namedtuple‬ها ‪ ،‬نمغتوانمرد آن را‬
‫به روزرسانغ یا اصالح پنمد‪ .‬تمام ویژگغها موجود روی شرغء ‪ namedtuple‬از اصرل‬
‫"یابار بنویس‪ ،‬چند بار بخوان" پمروی مغپنند‪.‬‬
‫عالوه بر این‪namedtuple ،‬ها‪ ،‬تاپلهای خوب نامگذاری شده هستند‪ .‬به هرر شرغء‬
‫ذخمره شده در آنها مغتوان از طریق شناسه منحصر به فرد (و قابرل فهرم بررای انسران‪)1‬‬
‫دسترسغ پمدا پرد‪ .‬این پار شما را از یادآوری اندیسهرای عردد صرحمح یرا اسرتفاده از‬
‫راهحلهایغ مانند تعممن ابتهای عدد صحمح بصورت پلمات یرادآور بررای انردیسها‬
‫رها مغپند‪.‬‬
‫حال‪ namedtuple ،‬به چه شالغ است؟‬

‫‪>>> from collections import namedtuple‬‬


‫)'‪>>> Car = namedtuple('Car' , 'color mileage‬‬

‫‪namedtuple‬ها در پایتون ‪ 2 .6‬به پتابخانره اسرتاندارد پرایتون افرزوده شردند‪ .‬بررای‬


‫استفاده از آنها‪ ،‬باید ماژول ‪ collections‬را ‪ import‬پنمد‪ .‬در مثال فوق‪ ،‬نو سادهای از‬

‫‪1 Human readable‬‬


‫‪  144‬ترفندهای پایتون‬

‫شغء ‪ Car‬را با دو فملد ‪( color‬رنگ) و ‪( mileage‬مسافت پمموده شده برحسرب مایرل)‬


‫تعریف پردم‪.‬‬
‫شرراید تعزرربآور باشررد پرره چرررا رشررته '‪ 'car‬را برره عنرروان اول رمن آرگومرران تررابع‬
‫‪ namedtuple‬در این مثال انتخاب مغپنم‪.‬‬
‫در مستندات پایتون به این پرارامتر "‪ " typename‬گفتره مرغشرود‪ .‬ایرن نرام پرالس‬
‫جدیدی است په با فراخوانغ تابع ‪ namedtuple‬ایزاد مغشود‪.‬‬
‫داده شرده بره پرالس‬ ‫چون ‪ namedtuple‬روشغ برای دانستن نرام متغمرر اختصرا‬
‫حاصل از آن ندارد‪ ،‬پس باید صریحا به آن بگویمم پره قصرد داریرم از چره نرامغ بررای‬
‫پررالس اسررتفاده پن رمم‪ .‬نررام پررالس در داکاسررترینگ و پمررادهسررازی __‪ __repr‬پرره‬
‫‪ namedtuple‬بطور خودپار برای ما ایزاد مغپند‪ ،‬استفاده شده است‪.‬‬
‫در این مثال‪ ،‬نابهنزاری دستوری دیگری وجود دارد‪ .‬چرا فملدها را به صورت رشرته‬
‫ای انتخاب مغپنمم په نامهای آنها را بصورت '‪ 'car mileage‬پدگذاری پند؟‬
‫پاس این است په تابع ‪ namedtuple‬تابع )(‪ split‬را روی نامهرای رشرته فراخروانغ‬
‫مغپند تا رشته آن را به لمستغ از نامها‪ ،‬تززیه پند‪ .‬پس این در واقع پوتاهنویسرغ شرده‬
‫قطعه پد زیر است‪.‬‬

‫)(‪>>> 'color mileage'.split‬‬


‫]'‪['color', 'mileage‬‬
‫)]'‪>>> Car = namedtuple('Car', ['color', 'mileage‬‬

‫البته‪ ،‬مغتوانمد لمستغ را مستقمما با نامهای رشتهها وارد پنمد‪ .‬مزیت استفاده از لمسرت‬
‫این است په اگر بخواهمد آن را به چندین خط تززیه پنمد‪ ،‬این پار سادهتر خواهد بود‪.‬‬

‫[ ‪>>> Car = namedtuple('Car',‬‬


‫‪'color',‬‬
‫‪'mileage',‬‬
‫)]‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪145 ‬‬

‫حال مغتوانمد به راحتغ شغء جدید ‪ Car‬را ایزاد پنمد‪ .‬شغء ‪ car‬طروری رفترار مرغ‬
‫پند په گویغ پالس ‪ Car‬را به صورت دستغ تعریف پردهاید و به آن سازندهای داده‪-‬‬
‫اید په "رنگ" و مقدار "مسافت پمموده شده بر حسب مایل" را مغپذیرد‪.‬‬

‫)‪>>> my_car = Car('red', 3812.4‬‬


‫‪>>> my_car.color‬‬
‫'‪'red‬‬
‫‪>>> my_car.mileage‬‬
‫‪3812.4‬‬

‫عالوه بر دسترسغ به مقادیر ذخمره شده در شناسه ‪ ،namedtuple‬مغتوانمد از طریق‬


‫اندیس به آنها نمز دسترسغ پمدا پنمد‪ .‬بدین طریق‪ namedtuple ،‬را مغتروان بصرورت‬
‫جایگزینغ برای ‪ tuple‬های معمولغ استفاده پرد‪.‬‬

‫]‪>>> my_car[0‬‬
‫'‪'red‬‬
‫)‪>>> tuple(my_car‬‬
‫)‪('red', 3812.4‬‬

‫باز پردن ‪ tuple‬و عملگر * برای بازپردن آرگومانهای ترابع نمرز طبرق انتظرار پرار‬
‫مغپند‪.‬‬

‫>>>‬ ‫‪color, mileage = my_car‬‬


‫>>>‬ ‫)‪print(color, mileage‬‬
‫‪red‬‬ ‫‪3812.4‬‬
‫>>>‬ ‫)‪print(*my_car‬‬
‫‪red‬‬ ‫‪3812.4‬‬

‫حتغ یاغ از فوایرد اسرتفاده از ‪ namedtuple‬هرا ایرن اسرت پره مغتوانمرد نمرایی‬
‫رشتهای زیبایغ برای اشماء اختصاصغ بدون همک زحمتغ داشته باشمد‪.‬‬

‫‪>>> my_car‬‬
‫)‪Car(color='red' , mileage=3812.4‬‬
‫‪  146‬ترفندهای پایتون‬

‫مانند تاپلها‪ namedtuple ،‬ها تغممرناپرذیر هسرتند‪ .‬اگرر یارغ از فملردهای آنهرا را‬
‫بازنویسغ پنمد‪ ،‬استثنا ‪ AttributeError‬را دریافت خواهمد پرد‪.‬‬

‫'‪>>> my_car.color = 'blue‬‬


‫"‪AttributeError: "can't set attribute‬‬

‫اشماء ‪ namedtuple‬بصورت پالسهای معمولغ داخلغ در پایتون پمرادهسرازی مرغ‬


‫شوند‪ .‬همچنمن از نظر بهمنگغ حافظه‪ ،‬بهترر از پالسهرای معمرولغ پرایتون هسرتند و بره‬
‫اندازه ‪tuple‬های معمولغ از حافظه پارآمدی برخوردار هستند‪.‬‬
‫یک روش خوب برای مشاهده آنها این اسرت پره تصرور پنمرد ‪ namedtuple‬هرا‬
‫ممانبری حافظه پارآمد‪ ،‬برای تعریف پالسهای تغممرناپذیر در پایتون هستند‪.‬‬

‫‪ .2-6-4‬ایجاد زیرکالس برای ‪ namedtuple‬ها‬


‫چون ‪ namedtuple‬ها روی پالسهای معمولغ پایتون ساخته مغشروند‪ ،‬حترغ مرغ‬
‫توانمد متدهایغ را به شغء ‪ namedtuple‬اضافه پنمد‪.‬‬
‫برای مثال‪ ،‬مغتوانمد مانند هرر پرالس دیگرری‪ ،‬پرالس ‪ namedtuple‬را گسرترش‬
‫دهمد و متدها و ویژگغهای جدیدی را بره آن اضرافه پنمرد‪ .‬در ادامره مثرالغ ارائره شرده‬
‫است‪.‬‬

‫)'‪Car = namedtuple('Car', 'color mileage‬‬

‫‪class MyCarWithMethods(Car):‬‬
‫‪def hexcolor(self):‬‬
‫‪if self.color == 'red':‬‬
‫'‪return '#ff0000‬‬
‫‪else:‬‬
‫'‪return '#000000‬‬

‫حال مغتوانمم شغء ‪ MyCarWithMethods‬را نمونهسازی پنمم و متد )(‪hexcolor‬‬


‫آن را فراخوانغ پنمم‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪147 ‬‬

‫)‪>>> c = MyCarWithMethods('red', 1234‬‬


‫)(‪>>> c.hexcolor‬‬
‫'‪'#ff0000‬‬

‫با این حال‪ ،‬این ممان است پمغ ناخوشرایند باشرد‪ .‬شراید اگرر بخواهمرد پرالس برا‬
‫خصوصمات تغممرناپذیر داشرته باشرمد‪ ،‬ارزش انزرام ایرن پرار را دارد‪ ،‬همچنرمن در ایرن‬
‫شرایط ممان است خود را در وضعمت بدتری قرار دهمد‪.‬‬
‫برای مثال‪ ،‬اضافه پردن فملد تغممرناپذیر جدید به دلمل نحوه سراخت ‪ namedtuple‬هرا‬
‫مستلزم دقت زیادی است‪ .‬سادهترین روش برای ایزاد سلسله مرتبه ای از ‪ namedtuple‬ها‬
‫استفاده از ویژگغ ‪ _fields‬در تاپل های پایه پایتون است‪.‬‬

‫)'‪>>> Car = namedtuple('Car', 'color mileage‬‬


‫(‪>>> ElectricCar = namedtuple‬‬
‫))‪'ElectricCar', Car._fields + ('charge',‬‬

‫از این پار نتمزه مطلوب زیر بدست مغآید‪.‬‬

‫)‪>>> ElectricCar('red', 1234, 45.0‬‬


‫)‪ElectricCar(color='red', mileage=1234, charge=45.0‬‬

‫‪ .3-6-4‬متدهای کمکی درونی‪ 1‬در پایتون‬


‫عالوه بر ویژگغ ‪ ،_fields‬هر نمونه ‪ namedtuple‬چند متد پماغ دیگری نمز ارائه‬
‫مغپند په ممان است مفمد باشند‪ .‬نامهای آنها همگغ برا یرک پراراپتر زیررخط (_)‬
‫شرو مغشوند په معموال آن متد یا ویژگغ پره "خصوصرغ" اسرت و بخشرغ از رابرط‬
‫عمومغ پالس یا ماژول نمست را عالمتگذاری مغپند‪.‬‬
‫با ‪namedtuple‬ها‪ ،‬قرارداد نامگذاری زیررخط معنرای متفراوتغ دارد‪ .‬ایرن متردها و‬
‫ویژگغهررای پماررغ‪ ،‬بخشررغ از رابررط عمررومغ ‪ namedtuple‬هررا هسررتند‪ .‬مترردها و‬
‫ویژگغهای پماغ برای جلوگمری از تداخل نامگذاریها با فملدهای تاپل تعریف شده‬

‫‪1 Built-in Helper Methods‬‬


‫‪  148‬ترفندهای پایتون‬

‫توسط پاربر‪ ،‬بدین صورت نامگذاری شدند‪ .‬بنابراین ادامه دهمد و در صورت نماز از آن‬
‫ها استفاده پنمد! نگران خصوصغ بودنشان نباشمد!‬
‫مغخواهم چند سناریو به شما نشان دهم پره در آن متردهای پمارغ ‪namedtuple‬‬
‫ممان است سودمند باشد‪ .‬اجازه دهمد با متد پماغ )(‪ _asdict‬شررو پنرمم‪ .‬ایرن پرار‬
‫محتویات ‪ namedtuple‬را بصورت دیاشنری برمغگرداند‪.‬‬

‫)(‪>>> my_car._asdict‬‬
‫)])‪OrderedDict([('color', 'red'), ('mileage', 3812.4‬‬

‫این برای جلوگمری از اشتباه تایپغ نام فملدها در هنگام ایزراد خروجرغ ‪ JSON‬بسرمار‬
‫عالغ است‪ ،‬برای مثال به قطعه پد زیر دقت پنمد‪.‬‬

‫))(‪>>> json.dumps(my_car._asdict‬‬
‫'}‪'{"color": "red", "mileage": 3812.4‬‬

‫دیگر متد پماغ مفمد تابع )(‪ _replace‬است‪ .‬این یک پپغ (سطحغ) از تاپل ایزراد‬
‫مغپند و به شما اماان جایگزینغ انتخابغ برخغ فملدهای آن را مغدهد‪.‬‬

‫)'‪>>> my_car._replace(color='blue‬‬
‫)‪Car(color='blue', mileage=3812.4‬‬

‫و در پایان‪ ،‬مغتوان از متد )(‪ _make‬برای ایزاد نمونههای جدیرد ‪ namedtuple‬در‬


‫توالغ یا تارار استفاده پرد‪.‬‬

‫)]‪>>> Car._make(['red', 999‬‬


‫)‪Car(color='red', mileage=999‬‬

‫‪ .4-6-4‬چه زمانی باید از ‪ namedtuple‬ها استفاده کنیم؟‬


‫عبارت ‪ namedtuple‬مغتواند روشغ ساده برای پاپسازی پد شرما و خوانرا پرردن‬
‫آن با اجرای ساختار بهتری برای دادههای شما باشد‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪149 ‬‬

‫برای مثال‪ ،‬فهممدم په استفاده از انوا دادههای موقت ماننرد دیاشرنریها برا فرمرت‬
‫ابت به ‪ namedtuple‬ها به من پمک مغپند تا اهداف خود را با وضوح بمشتری بمران‬
‫پنم‪ .‬ارلب وقتغ این سازماندهغ مزدد را انزام مغدهم‪ ،‬برای مسئلهای په با آن مواجره‬
‫هستم‪ ،‬راهحل جادویغ بهتری پمدا مغپنم‪.‬‬
‫همچنمن استفاده از ‪ namedtuple‬ها روی تاپلها و دیاشنریهای سراختاریافته مرغ‬
‫تواند زندگغ هماراران مرن را راحرتترر پنرد‪ ،‬چرون باعرث مرغشرود دادههرا پمرامرون‬
‫"خوداستنادی‪ "1‬پدهای نوشته شده عمل پنند‪.‬‬
‫از طرف دیگر‪ ،‬اگر آنها به من در نوشرتن پرد تممزترر و نگهداشرتپرذیرتر پمرک‬
‫نانند‪ ،‬از ‪ namedtuple‬ها به نفع خود استفاده نخواهم پررد‪ .‬ماننرد سرایر تانمرکهرای‬
‫نشان داده شده در این پتاب‪ ،‬گاهغ مغتوانند ناات بسمار خوبغ در این زممنه باشرند‪ .‬برا‬
‫این حال‪ ،‬اگر با احتماط مناسبغ از آنها استفاده پنمرد‪ ،‬برغشرک ‪ namedtuple‬هرا مرغ‬
‫توانند پد پایتون بهتر و گویاتری ایزاد پنند‪.‬‬

‫‪ .5-6-4‬نکات کلیدی ‪ namedtuple‬ها در پایتون‬


‫‪ ‬نررو داده ‪ collections.namedtuple‬ممررانبر حافظرره پارآمرردی برررای تعریررف‬
‫دستغ پالس تغممرناپذیر در پایتون است‪.‬‬
‫‪ ‬عبارت ‪ namedtuple‬با اعمال ساختار قابرل درک روی داده هرا‪ ،‬مرغتوانرد بره‬
‫پاپسازی پد شما پمک پند‪.‬‬
‫‪ ‬عبارت ‪ namedtuple‬چند متد پماغ مفمد په همگغ با یرک زیررخط شررو‬
‫مغشوند ارائه مغدهد په بخشغ از رابط عمومغ هستند‪ .‬استفاده از آنها اشاالغ‬
‫ندارد‪.‬‬

‫‪1 Self-documenting‬‬
‫‪  150‬ترفندهای پایتون‬

‫‪ .7-4‬مشکالت شیء کالس در مقابل نمونهای از کالس‬


‫عالوه بر ایزاد تمایز بمن متدهای پالس و متدهای نمونهای از پالس‪ ،‬همچنمن مدل‪،‬‬
‫اشماء پایتون تفاوت بمن شغء پالس و نمونهها را تشخمص مغدهند‪.‬‬
‫این نه تنها تمایز مهمغ است‪ ،‬بلاه سبب ایزاد مشال در برنامهنویسهرای ترازه پرار‬
‫پایتون نمز مغشود‪ .‬برای مدت زمانغ طوالنغ‪ ،‬به منظور درک این مفاهمم از مفاهمم پایه‪،‬‬
‫زمان موردنماز را سرمایه گذاری ناردم‪ .‬بنابراین آزماییهای اولمه ‪ OOP‬من با رفتارهای‬
‫رافلگمرانه و اشااالت اتفاقغ همراه شدند‪ .‬در این فصرل‪ ،‬هرگونره سرردرگمغ ادامرهدار‬
‫درباره این موضو را با برخغ نمونههای عملغ پاپسازی خواهمم پرد‪.‬‬
‫همانطور په گفتم‪ ،‬دو نو ویژگرغ داده روی اشرماء پرایتون وجرود دارد‪ .‬متغمرهرای‬
‫پالس ‪1‬و متغمرهای نمونه‪.2‬‬
‫متغمرهای پالس در داخل تعریف پالس اعالم مغشوند‪ .‬آنها با همک نمونه خاصرغ‬
‫از پالس ارتباط ندارند‪ .‬از طرفغ‪ ،‬متغمرهای پالس محتویات خود را روی خود پالس‬
‫ذخمره مغپنند و همه اشماء ایزاد شده از پالس خا ‪ ،‬دسترسغ به مزموعه یاسانغ از‬
‫متغمرهای پالس را دارند‪ .‬برای مثال‪ ،‬این بدان معناسرت پره اصرالح متغمرهرای پرالس‬
‫همزمان بر تمام نمونههای آن شغء ا ر مغگذارد‪.‬‬
‫متغمرهای نمونه همواره با نمونه خاصغ از شغء مرتبط هستند‪ .‬محتویرات آنهرا روی‬
‫پالس اصلغ ذخمره نمغشوند‪ .‬بلاه روی هر شغء تارغ ایزراد شرده از پرالس ذخمرره‬
‫مغشوند‪ .‬بنابراین‪ ،‬محتویات متغمر نمونه پامالد مستقل از نمونه شغء بعردی اسرت‪ .‬پرس‪،‬‬
‫تغممر متغمرهای یک نمونه فقط روی یک نمونه از شغء ا ر مغگذارد‪.‬‬
‫خب‪ ،‬این پامالد انتزاعغ بود‪ .‬اپنون بهتر است مقداری پد بررسغ پنمم! اجازه دهمرد‬
‫مثال قدیمغ "سگ" را بررسغ پنمم‪ .‬به چند دلمرل‪ ،‬برنامرههرای آموزشرغ ‪ OOP‬هممشره‬

‫‪1 Class variables‬‬


‫‪2 Instance variables‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪151 ‬‬

‫برای نشان دادن ناته خود از اتومبملها یا حموانات خانگغ استفاده مغپنند و تززیره آن‬
‫ها با روشهای سنتغ مشال است‪.‬‬
‫یک سگ شاد به چه چمزی نماز دارد؟ چهار پا و یک نام‪.‬‬

‫‪class Dog:‬‬
‫‪num_legs = 4 # <- Class variable‬‬

‫‪def __init__(self, name):‬‬


‫‪self.name = name # <- Instance variable‬‬

‫بسمار خب‪ ،‬این نمایی شغگرا نمونهای از وضعمت سگغ په به تازگغ توضرمح داده‬
‫ام‪ ،‬است‪ .‬ایزاد نمونههای جدید سگ طبق انتظار پار مغپند و هر یک از آنها‪ ،‬متغمرر‬
‫نمونهای تحت عنوان نام دریافت مغپنند‪.‬‬

‫)'‪>>> jack = Dog('Jack‬‬


‫)'‪>>> jill = Dog('Jill‬‬
‫‪>>> jack.name, jill.name‬‬
‫)'‪('Jack', 'Jill‬‬

‫در صورت استفاده از متغمرهای پالس‪ ،‬انعطرافپرذیری بمشرتری وجرود دارد‪ .‬بررای‬
‫مثال مغتوانمد بره متغمرر پرالس ‪ num_legs‬روی هرر نمونره شرغء ‪ Dog‬یرا بره صرورت‬
‫مستقمم در خود پالس ‪ Dog‬دسترسغ داشته باشمد‪.‬‬

‫‪>>> jack.num_legs, jill.num_legs‬‬


‫)‪(4, 4‬‬
‫‪>>> Dog.num_legs‬‬
‫‪4‬‬

‫بررا ایررن حررال‪ ،‬در صررورت دسترسررغ برره متغمررر نمونرره از طریررق پررالس‪ ،‬بررا خطررای‬
‫هسرتند‬ ‫‪ AttributeError‬مواجه خواهمد شد‪ .‬متغمرهای نمونه برای هر نمونه شغء خا‬
‫و وقتغ متد سازنده __‪ __init‬را اجراء مغپند مقداردهغ مغشوند‪ .‬آنها حتغ در خود‬
‫پالس نمز وجود ندارند‪.‬‬
‫این تمایز اصلغ بمن متغمرهای پالس و متغمرهای نمونه است‪.‬‬
‫‪  152‬ترفندهای پایتون‬

‫‪>>> Dog.name‬‬
‫‪AttributeError:‬‬
‫"'‪"type object 'Dog' has no attribute 'name‬‬

‫خب‪ ،‬تا اینزا خوب بوده است‪.‬‬


‫فرض مغپنمم په جک‪ ،‬سگغ است په هنگام رذا خروردن خرود را بره مرایاروویو‬
‫نزدیک مغپند و در او یک جفت پا اضافغ رشد مغپند‪ .‬چگونه این مسئله را در جعبره‬
‫شنغ پوچک پد په تا بحال بدست آوردهایم نمایی مغدهمد؟‬
‫اولمن ایده برای حل مسئله‪ ،‬ممارن اسرت صررفا تغممرر متغمرر ‪ num_legs‬در پرالس‬
‫سگ باشد‪.‬‬

‫‪>>> Dog.num_legs = 6‬‬

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

‫‪>>> jack.num_legs, jill.num_legs‬‬


‫)‪(6, 6‬‬

‫بنابراین این پار عملارد چنردانغ نداشرت‪ .‬دلمرل پرار نارردن ایرن اسرت پره تغممرر‬
‫متغمرپالس در فضای نامهای پالس‪ ،‬بر همه نمونههرای پرالس ا رر مرغگرذارد‪ .‬اجرازه‬
‫دهمد به تغممر متغمرپالس برگردیم و بزای دادن یک جفت پاهای اضافغ به همره‪ ،‬فقرط‬
‫دهمم‪.‬‬ ‫به جک اختصا‬

‫‪>>> Dog.num_legs = 4‬‬


‫‪>>> jack.num_legs = 6‬‬

‫حال‪ ،‬این پار چه هموالهایغ خلق پرد؟ اجازه دهمد بررسغ پنمم‪.‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪153 ‬‬

‫‪>>> jack.num_legs, jill.num_legs, Dog.num_legs‬‬


‫)‪(6, 4, 4‬‬

‫خب‪ ،‬این نسبتا خوب به نظر مغرسد (گذشته از این واقعمت په به جک بمچاره چند‬
‫پای اضافه دادیم)‪ .‬اما چگونه این تغممر واقعا روی اشماء سگ ا ر گذاشته است؟‬
‫همانطور په مغبمنمد‪ ،‬در اینزا مشال این است په ضمن گرفتن نتمزره مردنظر خرود‬
‫(پاهای اضافغ برای جک)‪ ،‬متغمر نمونه ‪ num_legs‬را برای نمونه جک معرفغ پردیم و‬
‫حاال متغمر جدید نمونه ‪ num_legs‬روی متغمرپالسغ بره همرمن نرام سرایه مرغانردازد و‬
‫هنگام دسترسغ به نمونه شغء‪ ،‬آن را تحتالشعا قرار داده و پنهان مغپند‪.‬‬

‫‪>>> jack.num_legs, jack.__class__.num_legs‬‬


‫)‪(6, 4‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬متغمرهای پالس ظاهرا از همگامسازی خرارج شردند‪.‬‬


‫سبب وقو این امر این بود پره تغممرر دادن متغمرر ‪ jack.num_legs‬یرک متغمرنمونره برا‬
‫همان نام متغمرپالس ایزاد پرد‪.‬‬
‫آگاه شدن از آنچه در اینزا رخ داده‪ ،‬در پشت صحنه اهممت بسماری دارد‪ .‬در نهایت‬
‫قبل از ایناه محدوده سطح پالس و سطح نمونه را در پایتون بفهمم‪ ،‬این عملارد باعث‬
‫شد تا مشاالت زیادی وارد پدهای من شوند‪.‬‬
‫اگر بخواهم واقعمت را بگویم‪ ،‬تالش برای تغممر متغمرپالس از طریق نمونه شغء‪ ،‬برر‬
‫متغمر پالس اصلغ ما ا ر مغگذارد‪ ،‬په این از مشاالت ‪ OOP‬در پایتون است‪.‬‬

‫‪ .1-7-4‬مثال بدون سگ‬


‫با ایناه همک سگغ در ایزاد این فصل صدمه ندید‪ ،‬مغخرواهم مثرال عملرغ دیگرر از‬
‫چمزهای مفمدی په مغتوانمد با متغمرهای پرالس انزرام دهمرد را بررای شرما بمران پرنم‪.‬‬
‫چمزی په پمغ نزدیاتر به پاربردهای واقعغ برای متغمرهای پالس است‪.‬‬
‫‪  154‬ترفندهای پایتون‬

‫پالس ‪ CountedObject‬زیر تعداد دفعاتغ په در طول عمر برنامه نمونهسازی شرده‬


‫را پمگمری مغپند‪( .‬په درواقع معمار جالبغ برای دانستن عملارد برنامه است‪).‬‬

‫‪class CountedObject:‬‬
‫‪num_instances = 0‬‬

‫‪def __init__(self):‬‬
‫‪self.__class__.num_instances += 1‬‬

‫پالس ‪ CountedObject‬متغمرپالسغِ ‪ num_instances‬را حفرظ مرغپنرد پره بره‬


‫عنوان شمارنده مشترک عمل مغپند‪ .‬در هنگرام تعریرف پرالس‪ ،‬شرمارنده را بره صرفر‬
‫مقداردهغ مغپند و بعد آن را خالغ مغگذارد‪.‬‬
‫هر بار په نمونه جدیدی از ایرن پرالس ایزراد مرغپنمرد‪ ،‬وقترغ پرایتون بره صرورت‬
‫خودپار متد سازنده __‪ __init‬را اجراء مغپند‪ ،‬شمارنده مشترک را با عدد یک جمع‬
‫مغپند تا یک واحد افزایی یابد‪.‬‬

‫>>>‬ ‫‪CountedObject.num_instances‬‬
‫‪0‬‬
‫>>>‬ ‫‪CountedObject().num_instances‬‬
‫‪1‬‬
‫>>>‬ ‫‪CountedObject().num_instances‬‬
‫‪2‬‬
‫>>>‬ ‫‪CountedObject().num_instances‬‬
‫‪3‬‬
‫>>>‬ ‫‪CountedObject.num_instances‬‬
‫‪3‬‬

‫به ایناه باید این پد را با یک حلقه اجرا پنمد ترا مطمرئن شروید متغمرر شرمارنده در‬
‫پالس افزایی مغیابد‪ ،‬توجه داشته باشرمد‪ .‬اگرر سرازنده را بره شررح زیرر نوشرته برودم‪،‬‬
‫متحمل اشتباهاتغ در محاسبات مغشدم‪.‬‬

‫‪# WARNING: This implementation contains a bug‬‬

‫‪class BuggyCountedObject:‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪155 ‬‬

‫‪num_instances = 0‬‬

‫‪def __init__(self):‬‬
‫!!! ‪self.num_instances += 1 #‬‬

‫همانطور په مشاهده پردید‪ ،‬این پمادهسازی برد‪ ،‬هرگرز متغمرر شرمارنده مشرترک را‬
‫افزایی نمغدهد‪.‬‬

‫>>>‬ ‫‪BuggyCountedObject.num_instances‬‬
‫‪0‬‬
‫>>>‬ ‫‪BuggyCountedObject().num_instances‬‬
‫‪1‬‬
‫>>>‬ ‫‪BuggyCountedObject().num_instances‬‬
‫‪1‬‬
‫>>>‬ ‫‪BuggyCountedObject().num_instances‬‬
‫‪1‬‬
‫>>>‬ ‫‪BuggyCountedObject.num_instances‬‬
‫‪0‬‬

‫قطعا حاال مغتوانمد ببمنمد در پزا اشرتباه پرردهام‪ .‬ایرن پمرادهسرازی (دارای اشراال)‬
‫هرگز شمارنده مشترک را افزایی نمغدهد چون در مورد مثال "جک" په قبال توضمح‬
‫دادم مرتاب اشتباه شدم‪.‬‬
‫ایررن پمررادهسررازی پررارایغ نخواهررد داشررت‪ .‬چررون بطررور تصررادفغ متغمررر پررالس‬
‫‪ num_instances‬را با ایزاد متغمر نمونه با هممن نام در سازنده سایه زدم‪( .‬سایه انداختن‬
‫یک متغمر روی متغمر دیگر باعث تغممر آن خواهد شد‪).‬‬
‫این پار به درستغ مقدار جدید شمارنده را محاسبه مغپنرد (از ‪ 0‬ترا ‪ ، )1‬امرا سرپس‬
‫نتمزه را در متغمر نمونه ذخمره مغپند‪ .‬این بدان معنغ است په سایر نمونرههرای پرالس‬
‫هرگز مقدار به روز رسانغ شده شمارنده را نمغبمنند‪.‬‬
‫همانطور په مغبمنمد‪ ،‬انزام این پار اشرتباه محرض اسرت‪ .‬پرس در هنگرام مواجره برا‬
‫وضعمتغ مشترک در یک پالس‪ ،‬بهتر است مراقب باشرمد و انزرام بررسرغ در محردوده‬
‫پد را دو برابر پنمد‪ .‬تستهای خودپار و بازبمنغ دقمق پد به این پار بسمار پمرک مرغ‬
‫پند‪.‬‬
‫‪  156‬ترفندهای پایتون‬

‫با این وجود‪ ،‬اممدوارم ایناه چرا و چگونه متغمرهای پالس‪ ،‬به ررم وجود مشاالت‬
‫آنها مغتوانند در عمل ابزارهای مفمدی باشند را بتوانمد مشاهده پنمد‪ .‬موفق باشمد!‬

‫‪ .2-7-4‬نکات کلیدی متغیرهای شی در مقابل نمونه شی‬


‫‪ ‬متغمرهای پالسغ برای اشتراک گذاری دادهها با همه نمونههای موجود از شغء‬
‫هستند‪ .‬آنها متعلق به پالس هستند و متعلق به نمونه خاصغ نمستند و در بمن همه‬
‫نمونههای پالس به اشتراک گذاشته مغشوند‪.‬‬
‫‪ ‬تمام دادههای منحصر به فرد متعلق به متغمرهای نمونه‪ ،‬فقط متعلق به همان نمونره‬
‫هستند‪ .‬آنها به نمونههای تاغ شغء تعلق دارند و در سایر نمونههای پرالس بره‬
‫اشتراک گذاشته نمغشوند‪ .‬هر متغمر نمونه‪ ،‬یک واحد حافظه پشتمبان مختص بره‬
‫خود را بدست مغآورد‪.‬‬
‫‪ ‬چون متغمرهای پالس را مغتوان با متغمرهای نمونه با همان نام "سایه" انداخت‪،‬‬
‫بازنویسغ تصادفغ متغمرهای پالس به روشغ په اشااالت و رفتارهای رمرعادی‬
‫را در برنامه ایزاد پند‪ ،‬ساده است‪.‬‬

‫‪ .8-4‬نمونه‪ ،‬کالس و متدهای استاتیک به زبان ساده‬


‫در این فصل خواهمد دید په در پس متدهای پالس‪ ،1‬متدهای استاتمک‪ 2‬و متردهای‬
‫معمول یک نمونه‪ 3‬در پایتون چه اتفاقغ مغافتد‪.‬‬
‫اگر درک بصری از تفاوتهای این متدها داشته باشمد‪ ،‬مغتوانمد شغ گرایغ زیباتری‬
‫در پدهای پایتون طراحغ پنمد و ارتباط بمن بخی های پد‪ ،‬گویا و قابل فهم باشد‪.‬‬
‫اجازه دهمد با نوشتن یک پالس پایتونغ حاوی نمونههای ساده برای هر سه نو متد‬
‫شرو پنمم‪.‬‬

‫‪1 Class methods‬‬


‫‪2 Static methods‬‬
‫‪3 Regular instance methods‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪157 ‬‬

‫‪class MyClass:‬‬
‫‪def method(self):‬‬
‫‪return 'instance method called', self‬‬

‫‪@classmethod‬‬
‫‪def classmethod(cls):‬‬
‫‪return 'class method called', cls‬‬

‫‪@staticmethod‬‬
‫‪def staticmethod():‬‬
‫'‪return 'static method called‬‬

‫نکته قابل توجه برای کاربران پایتون ‪ :2‬دپوریتورهای ‪ @staticmethod‬و‬


‫‪ @classmethod‬از پایتون ‪ 2.4‬به بعد در دسترس هستند‪ .‬پس این مثال در پایتون ‪ 2‬نمرز‬
‫پار خواهد پرد‪ .‬در پایتون ‪ 2‬به جای اعالن ساده پالس ‪ ،MyClass‬الزم اسرت پرالس‬
‫را به سبک دیگری با سمنتاس )‪ MyClass(object‬تعریف پنمد‪.‬‬

‫‪ .1-8-4‬متدهای یک نمونه یا ‪Instance method‬‬

‫متد اول در ‪ ،MyClass‬به نام ‪ ،method‬یک متد معمول در یک نمونه پالس است‪.‬‬
‫این نو متد‪ ،‬متدی ساده است په معموال از آن استفاده خواهمد پرد‪ .‬مشاهده مرغپنمرد‬
‫په این متد از یک پارامتر یعنغ ‪ self‬استفاده مغپند په در هنگام فراخوانغ این مترد بره‬
‫نمونه ‪ MyClass‬اشاره مغپند‪ .‬البته‪ ،‬متدهای نمونه مغتوانند بمی از یک پارامتر را قبول‬
‫پنند‪.‬‬
‫‪  158‬ترفندهای پایتون‬

‫از طریق پارامتر ‪ ،self‬متدهای نمونه مغتوانند آزادانه بره ویژگغهرا و سرایر متردهای‬
‫روی همان شغ دسترسغ پمدا پنند‪ .‬این پار در هنگام تغممر حالرت ‪1‬یرک شرغء قردرت‬
‫زیادی برای پنترل برنامه مغدهد‪.‬‬
‫آنها نه تنها مغتوانند حالت شغ را تغممر دهند‪ ،‬بلاه متدهای نمونه مغتوانند از طریق‬
‫ویژگغ __‪ self.__class‬به خود پالس دسترسغ پمدا پنند‪ .‬ایرن بردان معنرغ اسرت پره‬
‫متدهای نمونه مغتوانند حالت پالس را تغممر دهند‪.‬‬

‫‪ .2-8-4‬متدهای کالس یا ‪Class methods‬‬

‫اجررازه دهمررد بررا روش دوم‪ MyClass.classmethod ،‬آشررنا شررویم‪ .‬ایررن متررد را بررا‬
‫دپوریتور ‪ @classmethod‬برای نشان دادن متد پالس عالمتگذاری پردیم‪.‬‬
‫به جای آرگومان ورودی ‪ ،self‬متدهای پالس آرگومان ‪ cls‬را دریافت مغپنند په‬
‫وقتغ این متد فراخوانغ مغشود به پالس اشاره مغپند و به یک نمونه شغء اشاره نمرغ‬
‫پند‪.‬‬
‫چون متدهای پالس فقط بره ایرن آرگومران ‪ cls‬دسترسرغ دارنرد‪ ،‬پرس نمرغتواننرد‬
‫وضعمت نمونه شغء را تغممر دهند‪ .‬این پار مستلزم دسترسغ به ‪ self‬است‪ .‬برا ایرن حرال‪،‬‬
‫متدهای پالس مغتوانند وضعمت پالس را په به تمام نمونههای پالس اعمال مغشود‪،‬‬
‫اصالح پنند‪.‬‬

‫‪ .3-8-4‬متدهای استاتیک یا ‪Static methods‬‬

‫متد سوم‪ MyClass.staticmethod ،‬است په با دپوریتور ‪ @staticmethod‬بررای‬


‫مشخص پردن این متد استاتمک‪ ،‬عالمتگذاری پردم‪.‬‬
‫این نو متد از آرگومان ‪ self‬یا ‪ cls‬استفاده نمغپند‪ .‬البته مغتوان پاری انزام داد تا‬
‫این متد پارامترهای دلخواهغ را بپذیرد‪ .‬در نتمزه‪ ،‬متد استاتمک نمغتواند حالت شرغ یرا‬
‫حالت پالس را اصالح پند‪ .‬متدهای استاتمک محدود به آنچه په دادهها مرغتواننرد بره‬

‫‪1 state‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪159 ‬‬

‫آن دسترسغ داشته باشند‪ ،‬هستند‪ .‬آنهرا عمردتا روشرغ بررای فضرای نرام متردهای شرما‬
‫مغباشند‪.‬‬

‫‪ .4-8-4‬تفاوت انواع متدها در عمل‬


‫مغدانم این بحث تا این لحظه پامالد نظری بوده است‪ .‬همچنمن بر این باور هستم پره‬
‫پدنویسغ و تمرین باعث درک بصری بهتری برای ممزان تفاوت انروا متردها در عمرل‬
‫است‪ .‬به هممن دلمل حاال دست به پار خواهمم شد‪.‬‬
‫ابتدا به بررسغ نحوه رفتار این متدها هنگام فراخوانغ آنها مغپردازیم‪ .‬با ایزاد نمونه‬
‫ای از پالس شرو مغپنمم و سپس سه متد مختلف را روی آن فراخوانغ مغپنمم‪.‬‬
‫شغء ‪ MyClass‬به گونهای پمادهسازی شده است پره در آن هرر مترد‪ ،‬حراوی ‪tuple‬‬
‫برای بازگرداندن اطالعات است‪ ،‬به صورتغ په بتروانمم نحروه اجررای برنامره را ردیرابغ‬
‫پنمم‪.‬‬
‫در متد نمونه ‪1‬زیر مشخص شده است په در هنگام فراخوانغ چه اتفاقغ مغافتد‪.‬‬

‫)(‪>>> obj = MyClass‬‬


‫)(‪>>> obj.method‬‬
‫‪('instance method called', <MyClass instance at‬‬
‫)>‪0x11a2‬‬

‫این پد نشان مغدهد په متد نمونه از طریق آرگومان ‪ self‬به نمونه شغء (چاپ شده‬
‫بصورت >‪ )<MyClass instance‬دسترسغ دارد‪.‬‬
‫هنگام فراخوانغ متد‪ ،‬پایتون آرگومان ‪ self‬را با شغء نمونه‪ obj ،‬جرایگزین مغپنرد‪.‬‬
‫مغتوانمم فرمت تغممر یافتره برا سرمنتاس فراخروانغ نقطرهای )(‪ 2obj.method‬را نادیرده‬
‫بگمریم و آن را به صورت دستغ فراخوانغ پنمم تا نتمزه مشابه را بدست آوریم‪.‬‬

‫‪1 Instance Method‬‬


‫‪2 Dot-call‬‬
‫‪  160‬ترفندهای پایتون‬

‫)‪>>> MyClass.method(obj‬‬
‫‪('instance method called', <MyClass instance at‬‬
‫)>‪0x11a2‬‬

‫بدین ترتمب‪ ،‬متدهای نمونره مرغتواننرد از طریرق ویژگرغ __‪ self.__class‬بره خرود‬
‫پالس دسترسغ پمدا پنند‪ .‬این پرار مترد نمونره را از لحراظ محردودیت هرای دسترسرغ‬
‫قدرتمند مغپند‪ .‬آنها مغتوانند آزادانه حالت روی نمونه شغء و خود پالس را اصالح‬
‫پنند‪.‬‬
‫اجازه دهمد در ادامه متدهای پالس را بررسغ پنمم‪.‬‬

‫)(‪>>> obj.classmethod‬‬
‫)>‪('class method called', <class MyClass at 0x11a2‬‬

‫فراخوانغ )(‪ classmethod‬به ما نشان مغدهد پره بره شرغء >‪<MyClass instance‬‬
‫دسترسغ ندارد‪ .‬بلاه فقط به شغء <‪ >class MyClass‬دسترسغ دارد‪ ،‬پره بمرانگر خرود‬
‫پالس مغباشد (همه چمز حتغ خود پالسها در پایتون شغء هستند)‪.‬‬
‫به ایناره چگونره پرایتون هنگرام فراخروانغ )(‪ ،MyClass.classmethod‬بره صرورت‬
‫خودپار پالس را بعنوان اولمن آرگومان بره ترابع مررتبط مغپنرد‪ ،‬توجره داشرته باشرمد‪.‬‬
‫فراخوانغ یک متد در پرایتون از طریرق سرمنتاس نقطرهای‪ ،‬سربب ایرن رفترار مرغشرود‪.‬‬
‫آرگومان ‪ self‬در متد نمونه به هممن طریق عمل مغپند‪.‬‬
‫لطفا توجه داشته باشمد په نام گذاری آرگومانهای ‪ self‬و ‪ cls‬قراردادی اسرت‪ .‬مرغ‬
‫توانمد به راحتغ آنها را به صورت ‪ the_object‬و ‪ the_class‬نام گذاری پنمد و نتمزه‬
‫مشابه را بدست آورید‪ .‬تنها چمزی په اهممت دارد این اسرت پره آنهرا ابتردا در لمسرت‬
‫قرار مغگمرند‪.‬‬ ‫پارامتر برای آن متد خا‬
‫حال زمان فراخوانغ از طریق متد استاتمک است‪.‬‬

‫)(‪>>> obj.staticmethod‬‬
‫'‪'static method called‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪161 ‬‬

‫دیدید په چگونه )(‪ staticmethod‬را روی شرغء فراخروانغ پرردیم و توانسرتمم برا‬
‫موفقمت این پار را انزام دهمم‪ .‬برخغ برنامهنویس ها وقتغ مغفهمنرد فراخروانغ برا مترد‬
‫استاتمک روی نمونه شغء اماانپذیر است‪ ،‬شگفت زده مغشوند‪.‬‬
‫در پشت صحنه‪ ،‬هنگام فراخوانغ متد استاتمک با استفاده از سمنتاس نقطهای‪ ،‬پایتون‬
‫با عبور دادن ‪ self‬یا آرگومان ‪ cls‬صرفا محدودیت های دسترسغ را اعمال مغپند‪.‬‬
‫این امر نشانگر این است په متدهای استاتمک نه مغتوانند به حالت نمونه شرغء و نره‬
‫حالت پالس دسترسغ داشته باشند‪ .‬آنها مانند توابع معمولغ پار مغپنند‪ ،‬اما متعلرق بره‬
‫فضای نام پالس (و هر نمونه) هستند‪.‬‬
‫حال‪ ،‬به بررسغ مواردی خواهمم پرداخت په هنگام فراخوانغ این متدها روی شرغء‬
‫خود پالس رخ مغدهد‪ ،‬بدون ایناه از قبل یک نمونه شغء ایزاد پنمم‪.‬‬

‫)(‪>>> MyClass.classmethod‬‬
‫)>‪('class method called', <class MyClass at 0x11a2‬‬

‫)(‪>>> MyClass.staticmethod‬‬
‫'‪'static method called‬‬

‫)(‪>>> MyClass.method‬‬
‫‪TypeError: """unbound method method() must‬‬
‫‪be called with MyClass instance as first‬‬
‫""")‪argument (got nothing instead‬‬

‫مغتروانمم )(‪ classmethod‬و )(‪ staticmethod‬را بره راحترغ فراخروانغ پنرمم‪ ،‬امرا‬
‫فراخوانغ متد )(‪ method‬با خطای ‪ TypeError‬ناموفق است‪.‬‬
‫این چمزی است په انتظار آن مغرفت‪ .‬این بار نمونه شرغء را ایزراد نارردیم و ترابع‬
‫نمونه را مستقمما روی خود پالس فراخوانغ پردیم‪ .‬این بدان معنغ است په همک راهرغ‬
‫در پایتون برای ایزاد آرگومان ‪ self‬وجود ندارد‪ ،‬پس فراخوانغ برا اسرتثناء ‪TypeError‬‬
‫انزام نمغشود‪.‬‬
‫‪  162‬ترفندهای پایتون‬

‫این پار باعث تمایز آشاار بمن این سه نو روش مغشود‪ .‬امرا نگرران نباشرمد‪ ،‬قصرد‬
‫ندارم این موضو را در اینزا پایان دهم‪ .‬در دو بخی بعدی‪ ،‬بره بررسرغ دو مثرال پمرغ‬
‫خواهم پرداخت‪.‬‬ ‫واقع گرایانهتر از زمان استفاده از این نو متد خا‬
‫مبنای مثال های خود را پمرامون یک پالس پمتزا قرار خواهم داد‪.‬‬

‫‪class Pizza:‬‬
‫‪def __init__(self, ingredients):‬‬
‫‪self.ingredients = ingredients‬‬

‫‪def __repr__(self):‬‬
‫')}‪return f'Pizza({self.ingredients!r‬‬

‫)]'‪>>> Pizza(['cheese', 'tomatoes‬‬


‫)]'‪Pizza(['cheese', 'tomatoes‬‬

‫‪ .5-8-4‬تولید پیتزاهای خوشمزه با ‪@classmethod‬‬

‫همانطور په در واقعمت با پمتزا روبرو شدهاید‪ ،‬مغدانمد په متغمرهای بسمار خوشرمزه‬


‫ای وجود دارند‪.‬‬

‫)]'‪Pizza(['mozzarella', 'tomatoes‬‬
‫)]'‪Pizza(['mozzarella', 'tomatoes', 'ham', 'mushrooms‬‬
‫)‪Pizza(['mozzarella'] * 4‬‬

‫ایتالمایغها قرن ها پمی پمتزا را پشف پردند‪ ،‬پس انروا خوشرمزه پمترزا برا نرامهرای‬
‫آنها است‪ .‬در صورت بهرهمندی از نام آنها‪ ،‬عملارد بهتری خرواهمم داشرت و‬ ‫خا‬
‫به پاربران پالس پمتزا خود‪ ،‬رابط بهتر برای ایزاد اشماء پمتزایغ په طلب مغپننرد ارائره‬
‫مغدهمم‪.‬‬
‫روشغ خوب و تممز برای انزام این پار با استفاده از متدهای پالس به صورت توابع‬
‫پارخانه‪ 1‬برای انوا مختلف پمتزایغ په مغتوانمم ایزاد پنمم است‪.‬‬

‫)‪1 Factory design pattern (object-oriented programming‬‬


‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪163 ‬‬

‫‪class Pizza:‬‬
‫‪def __init__(self, ingredients):‬‬
‫‪self.ingredients = ingredients‬‬

‫‪def __repr__(self):‬‬
‫')}‪return f'Pizza({self.ingredients!r‬‬

‫‪@classmethod‬‬
‫‪def margherita(cls):‬‬
‫)]'‪return cls(['mozzarella', 'tomatoes‬‬

‫‪@classmethod‬‬
‫‪def prosciutto(cls):‬‬
‫)]'‪return cls(['mozzarella', 'tomatoes', 'ham‬‬

‫به ایناه چگونه به جای فراخوانغ مستقمم سازنده پمتزا‪ ،‬از آرگومران ‪ cls‬در متردهای‬
‫پارخانه ‪ margherita‬و ‪ prosciutto‬استفاده مغپنم‪ ،‬توجه داشته باشمد‪.‬‬
‫این ترفندی است په مغتوانمد از آن برای دنبال پردن اصل "خودت را تارار نارن‬
‫(‪ ")DRY‬استفاده پنمد‪ .‬اگر تصممم بگمرریم ایرن پرالس را در برخرغ از نقراط تغممرر نرام‬
‫دهمم‪ ،‬الزم نمست به روز رسانغ نام سازنده در تمرام توابرع پارخانره را بره خراطر داشرته‬
‫باشمم‪.‬‬
‫حال‪ ،‬با این متدهای پارخانه چه پاری مغتوانمم انزام دهرمم؟ اجرازه دهمرد آنهرا را‬
‫بررسغ پنمم‪.‬‬

‫)(‪>>> Pizza.margherita‬‬
‫)]'‪Pizza(['mozzarella', 'tomatoes‬‬

‫)(‪>>> Pizza.prosciutto‬‬
‫)]'‪Pizza(['mozzarella', 'tomatoes', 'ham‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬مغتوانمم از توابع پارخانهای برای ایزاد اشماء جدیرد‬
‫پمتزا په دقمقا به روشغ درست مانند روشغ په ما مغخواهمم پماربنردی شروند‪ ،‬اسرتفاده‬
‫پنمم‪ .‬همه آنها در داخل از سازنده مشابه __‪ __init‬استفاده مغپننرد و فقرط ممرانبری‬
‫برای به خاطر آوردن همه مواد تشامل دهنده ارائه مغدهند‪.‬‬
‫‪  164‬ترفندهای پایتون‬

‫روشغ دیگر برای بررسغ استفاده از متدهای پالس‪ ،‬فهممدن این مطلب است په این‬
‫پار به شما اماان تعریف سازندههای جایگزین برای پالسهای شما را فراهم مغپند‪.‬‬
‫پایتون فقط از یک متد __‪ __init‬در هر پالس استفاده مغپند‪ .‬استفاده از متردهای‬
‫پالس‪ ،‬افزودن سازندههای جایگزین بمشتر را در صورت لزوم اماانپذیر مغپنرد‪ .‬ایرن‬
‫پار مغتواند رابطغ برای خوداستنادی پالسهای شما (تا انردازه معمنرغ) و سرادهسرازی‬
‫پاربرد آنها ایزاد پند‪.‬‬

‫‪ .6-8-4‬چه موقع باید از متدهای استاتیک استفاده کنیم‬


‫در اینزا‪ ،‬ارزیابغ مثال مناسب پمغ دشوارتر است‪ ،‬اما باید به شرما بگرویم پره فقرط‬
‫مقایسه پمتزای هر چه نازپتر را ادامه خواهم داد‪( .‬یامغ‪)!1‬‬
‫این چمزی است په باید در اینزا ارزیابغ پنمم‪.‬‬

‫‪import math‬‬

‫‪class Pizza:‬‬
‫‪def __init__(self, radius, ingredients):‬‬
‫‪self.radius = radius‬‬
‫‪self.ingredients = ingredients‬‬

‫‪def __repr__(self):‬‬
‫' ‪return (f'Pizza({self.radius!r},‬‬
‫)')}‪f'{self.ingredients!r‬‬

‫‪def area(self):‬‬
‫)‪return self.circle_area(self.radius‬‬

‫‪@staticmethod‬‬
‫‪def circle_area(r):‬‬
‫‪return r ** 2 * math.pi‬‬

‫‪1 Yummy‬‬
‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪165 ‬‬

‫حال در این رابطه چه تغممرری انزرام دادم؟ ابتردا‪ ،‬سرازنده و مترد __‪ __repr‬بررای‬
‫پذیرفتن آرگومان شعا اضافغ را اضافه پردم‪.‬‬
‫همچنمن متد نمونه )(‪ area‬را اضافه پردم په مساحت پمتزا را محاسبه پرده و برازمغ‬
‫گرداند‪ .‬این پار همچنمن جایگزین مناسبغ برای ‪ @property‬خواهد بود‪ .‬اما این فقرط‬
‫مثالغ سرگرمپننده است‪.‬‬
‫بزای محاسبه مستقمم مساحت در داخل )(‪ ،area‬برا اسرتفاده از فرمرول شرناخته شرده‬
‫مساحت دایره‪ ،‬این روش را به متد استاتمک جداگانه )(‪ circle_area‬تبدیل پردم‪.‬‬
‫اجازه دهمد این روش را بررسغ پنمم‪.‬‬

‫)]'‪>>> p = Pizza(4, ['mozzarella', 'tomatoes‬‬


‫‪>>> p‬‬
‫)}‪Pizza(4, {self.ingredients‬‬
‫)(‪>>> p.area‬‬
‫‪50.26548245743669‬‬
‫)‪>>> Pizza.circle_area(4‬‬
‫‪50.26548245743669‬‬

‫قطعا‪ ،‬این هنوز یک مثال ساده است‪ ،‬اما بره توضرمح برخرغ از مزایرایغ پره متردهای‬
‫استاتمک ارائه مغدهند پمک مغپند‪.‬‬
‫طبق آموختههای ما‪ ،‬متدهای استاتمک نمغتوانند به حالرت پرالس یرا حالرت نمونره‬
‫دسترسغ پمدا پنند‪ ،‬چون آرگومان ‪ cls‬یرا ‪ self‬را دریافرت نمرغپننرد‪ .‬ایرن محردودیت‬
‫از‬ ‫بزرگغ است‪ .‬همچنمن سمگنالغ عالغ برای نشان دادن مستقل بودن این متدهای خا‬
‫هر چمز پمرامون آن است‪.‬‬
‫در مثال فوق‪ ،‬مشخص است په متد )(‪ circle_area‬به همک وجه نمغتواند پالس یرا‬
‫نمونه پالس را اصالح پند‪( .‬قطعا‪ ،‬همواره مغتوانمد پمرامون آن با متغمر پلغ پار پنمد‪،‬‬
‫اما در اینزا موضو مهمغ نمست‪).‬‬
‫حاال‪ ،‬چرا این پار حائز اهممت است؟‬
‫‪  166‬ترفندهای پایتون‬

‫نام گذاری یک مترد بره عنروان مترد اسرتاتمک‪ ،‬فقرط نشرانه ایرن نمسرت پره آن مترد‬
‫نمغتواند حالت پالس یا حالت نمونه را اصالح پند‪ .‬همانطور په مشاهده پردید‪ ،‬ایرن‬
‫محدودیت در زمان اجرای پایتون نمز اعمال مغشود‪.‬‬
‫تانمک هایغ مانند این‪ ،‬اماان برقراری ارتبراط مسرتقمم برا بخری هرایغ از معمراری‬
‫پالس را فرراهم مرغپننرد ترا پرار توسرعه جدیرد بره صرورت طبمعرغ و برا وجرود ایرن‬
‫محدودیتها انزام شود‪ .‬البته‪ ،‬حذف این محدودیت ها پار بسمار آسانغ خواهد بود‪ .‬اما‬
‫در عمل‪ ،‬آنها به جلوگمری از اصالحات تصرادفغ پره بررخالف طررح اصرلغ هسرتند‪،‬‬
‫پمک مغپنند‪.‬‬
‫به بمانغ متفاوت‪ ،‬استفاده از متدهای استاتمک و متردهای پرالس‪ ،‬روش هرایغ بررای‬
‫برقراری ارتباط با هدف برنامهنویس هستند و هدف را به اندازه مناسب مشخص مغپنند‬
‫تا از اشتباهات و اشااالت "فراموشغ" په طرح را خراب مغپند‪ ،‬اجتناب شود‪.‬‬
‫با پاربرد به ممزان پم و منطقغ‪ ،‬نوشتن برخغ متدها به این صورت مغتوانند مزایرای‬
‫نگهداری را فراهم پنند و احتمال استفاده ناصحمح سایر برنامرهنرویسهرا از پالسهرای‬
‫شما را پاهی دهند‪.‬‬
‫همچنمن متدهای استاتمک هنگام نوشتن یونمرت تسرتها مزایرایغ دارنرد‪ .‬چرون مترد‬
‫)(‪ circle_area‬پامالد مستقل از بقمه پالس است‪ ،‬پس آزمایی آن بسمار سادهتر خواهد‬
‫بود‪.‬‬
‫از طرف دیگر‪ ،‬این پار فرآیندهای آتغ نگهداری را آسانتر مغپند و پمونردی برمن‬
‫سبک برنامهنویسغ شغگرا و رویهای‪ 1‬ایزاد مغپند‪.‬‬

‫‪ .7-8-4‬نکات کلیدی در هنگام کار با انواع متدها‬


‫• متدهای نمونه بره نمونره پالسرغ نمراز دارنرد و مرغتواننرد از طریرق ‪ self‬بره نمونره‬
‫دسترسغ پمدا پنند‪.‬‬

‫‪1 Procedural programming style‬‬


‫فصل چهارم‪ :‬کالسها و برنامهنویسی شیء گرا ‪167 ‬‬

‫• متدهای پالس به نمونه پالس نمرازی ندارنرد‪ .‬آنهرا نمرغتواننرد بره نمونره (‪)self‬‬
‫دسترسغ پمدا پنند اما از طریق ‪ cls‬به خود پالس دسترسغ دارند‪.‬‬
‫• متدهای استاتمک به ‪ cls‬یا ‪ self‬دسترسغ ندارند‪ .‬آنها مانند توابع معمولغ پار مرغ‬
‫پنند‪ ،‬اما متعلق به فضای نام پالس هستند‪.‬‬
‫• متدهای استاتمک و پالس با یادیگر ارتباط برقرار مغپننرد و (ترا انردازه معمنرغ)‬
‫هدف برنامهنویس را درباره طراحغ پالس اجراء مغپننرد‪ .‬ایرن امرر مرغتوانرد مزایرای‬
‫قطعغ در زممنه نگهداری از سمستم را ایزاد پند‪.‬‬
‫فصل پنزم‪ :‬ساختارهای داده در پایتون‬

‫فصل پنجم‬

‫ساختارهای داده در پایتون‬


‫یک برنامه نویس پایتون باید بر ساختارهای داده‪ 1‬پامالد مسلط باشد‪ .‬ساختارهای داده‬
‫یاغ از پایهای ترین اجزای برنامهی شما بوده و هرپدام از آنها روش خاصرغ را بررای‬
‫سازماندهغ دادههای شما فراهم مغپنند؛ بنرابراین بسرته بره نمراز‪ ،‬مغتروان از هرپردام از‬
‫آنها در موقعمتغ مناسب استفاده نمود‪.‬‬
‫مرور برخغ از مباحث پایهای در پایتون‪ ،‬شاید در گام اول پمغ خسته پننده بره نظرر‬
‫برسد؛ اما به یاد داشته باشمد په با این پار قدرت درک خود را از این زبان باال بردهایرد‬
‫و بهتر مغتوانمد مفاهمم پمچمده را یاد بگمرید‪.‬‬
‫ما در پایتون ساختارهای دادهی مختلفغ ماننرد لمسرت‪ ،‬دیاشرنری‪ ،‬مزموعره (‪ )set‬و‬
‫… داریم‪ .‬اما پشته‪ 2‬چطور؟ آیا در پایتون پشته هم داریم؟‬
‫معموال برخغ از ساختارهای دادهای انتزاعغ مانند پشته در پایتون مشخص نمست پره‬
‫به چه صورت هستند‪ .‬برای مثال زبانغ مانند جاوا دارای انروا مختلفرغ از لمسرت اسرت‪:‬‬
‫‪ LinkedList‬یا ‪.ArrayList‬‬
‫این پار باعث فهم بهتر نو دادهها در جاوا مغشود‪ .‬پایتون به یک نامگرذاری سراده‬
‫بسنده مغپند و هممن باعث جذابمت و سادگغ این زبان برنامه نویسغ مغشود‪ .‬اما نقطره‬
‫ضررعف زمررانغ مشررخص مغشررود پرره صررحبت از لمسررت در پررایتون اسررت‪ ،‬منظررور مررا‬
‫‪ LinkedList‬است یا ‪.ArrayList‬‬
‫ما در این فصل به بحرث در مرورد سراختارهای دادهی پایرهای و پمراده سرازی انروا‬
‫دادههای انتزاعغ‪ 3‬در پایتون مغپردازیم‪ .‬هردف مرا ایرن اسرت ترا دریرابمم معرادل انروا‬
‫داده های انتزاعغ در پایتون به چه صورت است و در مورد هرپدام از آنهرا توضرمحات‬
‫مختصری ارایه مغدهمم‪.‬‬

‫‪1 Data Structures‬‬


‫‪2 Stack‬‬
‫)‪3 Abstract Data Types (ADTs‬‬
‫‪  172‬ترفندهای پایتون‬

‫‪Dictionary .1-5‬ها‪Map ،‬ها و ‪Hashtable‬ها‬


‫دیاشنریها در پایتون مغتوانند تعدادی آبزات‪ 1‬در خود ذخمره پنند په هرپردام‬
‫از آنها با یک پلمد‪ 2‬قابل دسترسغ هستند‪.‬‬
‫دیاشنریها معموال با نامهرایغ ماننرد ‪ maps‬یرا ‪ hashmaps‬یرا ‪ lookup tables‬نمرز‬
‫شناخته مغشوند په به ما اجازه مغدهند تا آبزاتغ جدید را براساس یک پلمرد حرذف‬
‫پرده‪ ،‬اضافه پنمم و یا بمابمم‪.‬‬
‫بمایمد یک دفترچهی تلفن را در نظر بگمریم‪ .‬با استفاده از یک دفترچه تلفن مغتوانمم‬
‫اطالعات مربوط به یک فرد را به سادگغ براساس یک پلمرد مشرخص (نرام فررد مرورد‬
‫نظر) به دست آوریم‪.‬‬
‫دیاشنریها به ما این اجازه را مغدهند تا به سرعت اطالعرات مررتبط برا یرک پلمرد‬
‫را بدست آوریم‪ .‬به طور پلغ‪ ،‬دیاشنری یارغ از پایرهای تررین و اساسرغ تررین‬ ‫خا‬
‫ساختارهای داده در علوم پامپموتر است‪.‬‬

‫‪ - dict .1-1-5‬سادهترین نوع دیکشنری‬


‫نو دادهی ‪ dict‬به صرورت پرمی فررض در پرایتون پمراده سرازی شرده اسرت‪ .‬بررای‬
‫ساخت یک دیاشنری در پایتون از آپوالد استفاده مغشود‪.‬‬

‫‪1 object‬‬
‫‪2 key‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪173 ‬‬

‫{ = ‪phonebook‬‬
‫‪'bob': 7387,‬‬
‫‪'alice': 3719,‬‬
‫‪'jack': 7052,‬‬
‫}‬

‫})‪squares = {x: x * x for x in range(6‬‬

‫]'‪>>> phonebook['alice‬‬
‫‪3719‬‬

‫‪>>> squares‬‬
‫}‪{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25‬‬

‫اما محدودیتهایغ نمز در هنگام ساخت یک دیاشنری وجود دارد په تنها مغتروان‬
‫از برخغ از نو دادهها به عنوان پلمد استفاده پرد‪.‬‬
‫دیاشنریها در پایتون توسط پلمدهایغ ساخته مغشوند په ‪ hashable‬باشرند‪ :‬یرک‬
‫آبزات ‪ hashable‬یک مقدار هی دارد په در طول زمان تغممر نمغپند (__‪)__hash‬‬
‫و قابل قماس با سایر آبزاتها مغباشد‪ .‬عالوه بر آن‪ ،‬آبزاتهای ‪ hashable‬په با هم‬
‫برابرند (__‪ )__eq‬دارای مقدار هی یاسانغ هستند‪.‬‬
‫دادههای تغممر ناپذیر‪ 1‬مانند رشتهها یا اعداد ‪ hashable‬هستند و مغتوان از آنهرا بره‬
‫عنوان پلمد در دیاشنری استفاده نمود‪ .‬همچنمن از تاپلها‪ 2‬نمز (هنگرامغ پره محتویرات‬
‫آنها از نو ‪ hashable‬باشند) مغتوان به عنوان پلمد در دیاشنری استفاده پرد‪.‬‬
‫در اپثررر مرروارد نررو دادهی ‪ dict‬پرره پررایتون آن را برررای مررا فررراهم پرررده اسررت‪،‬‬
‫پاسخگوی تمام نمازهای ما است‪ .‬دیاشنریها بسمار بهمنه سازی شدهاند و در قسمتهای‬
‫مختلف زبان پایتون نمز وجود دارند‪ .‬به عنروان مثرال هنگرامغ پره شرما در پرایتون یرک‬
‫پالس ایزاد مغپنمد‪attribute ،‬های آن پالس به صورت دیاشنری در داخرل شرغء‬
‫پایتون ذخمره مغشوند‪.‬‬

‫‪1 immutable‬‬
‫‪2 tuples‬‬
‫‪  174‬ترفندهای پایتون‬

‫هزینهی استفاده از دیاشنری برای اعمالغ ماننرد جسرتزو‪ ،‬حرذف‪ ،‬افرزودن یرا برروز‬
‫رسانغ یک آیتم بسمار پایمن است و به صورت بهمنه‪ ،‬پماده سرازی شرده اسرت‪ .‬پره ایرن‬
‫هزینه برابر با )‪ O(1‬است‪.‬‬
‫عالوه بر نو دادهی ‪ ،dict‬در پایتون دیاشنریهای دیگری مانند دیاشنری مبتنغ بر‬
‫‪ B-tree‬نمز وجود دارد په تمام آنها بر پایهی هممن پالس دیاشنری (‪ )dict‬در پایتون‬
‫هستند په در پنار تمام ویژگغهای این پالس‪ ،‬یاسری ویژگغهای اضافه تر را نمرز بره‬
‫همراه خود دارند‪ .‬در ادامه به برخغ از آنها نگاهغ مغاندازیم‪.‬‬

‫‪ - Collections.OrderedDict .2-1-5‬دیکشنری به همراه کلیدهای دارای‬


‫اولویت‬
‫این نو از دیاشنریها در پایتون‪ ،‬به سادگغ و با نوشتن نام پلمدها و مقدار آنها در‬
‫پایتون ساخته مغشوند‪ .‬هر زمان پلمد جدیدی به یک نو از این دیاشنری اضافه شود‪،‬‬
‫آن پلمد به عنوان آیتم آخر دیاشنری در نظر گرفته مغشود‪.‬‬
‫بنابراین درصورتغ په در الگوریتم برنامهی شما ترتمرب پلمردهای موجرود در یرک‬
‫دیاشنری اهممت دارد‪ ،‬مغتوانمد از پالس ‪ OrderedDict‬استفاده پنمد‪.‬‬
‫البته توجه داشته باشمد په این پالس جزء هستهی اصلغ زبان پرایتون نمسرت و بررای‬
‫استفاده از آن باید آن را از پتابخانهی ‪ collections‬ایمپورت پنمد‪.‬‬
175  ‫ ساختارهای داده در پایتون‬:‫فصل پنجم‬

>>> import collections


>>> d = collections.OrderedDict(one=1, two=2,
three=3)

>>> d
OrderedDict([('one', 1), ('two', 2), ('three', 3)])

>>> d['four'] = 4
>>> d
OrderedDict([('one', 1), ('two', 2),
('three', 3), ('four', 4)])

>>> d.keys()
odict_keys(['one', 'two', 'three', 'four'])

‫ بازگرداندن مقداری پیش فرض برای‬- collections.defaultdict .3-1-5


‫کلیدهای ناموجود‬
‫ مغتروان‬،‫نوعغ دیگری از دیاشنری در پایتون وجود دارد په در هنگام سراخت آن‬
‫ در هنگام دستمابغ به پلمردی پره در ایرن دیاشرنری‬.‫یک نو داده را به آن ارسال پرد‬
.‫ مقداری خالغ از آن نو داده برگشت داده مغشود‬،‫وجود ندارد‬
‫ به شما پمک مغپند تا بزای استفاده از یک دیاشنری معمولغ‬،‫این نو دیاشنری‬
‫ به سادگغ و بدون نگرانغ برا‬،get ‫ یا استفاده از متد‬KeyError ‫و دریافت خطاهایغ مانند‬
.‫دیاشنریهای خود پار پنمد‬

>>> from collections import defaultdict


>>> dd = defaultdict(list)

# Accessing a missing key creates it and


# initializes it using the default factory,
# i.e. list() in this example:
>>> dd['dogs'].append('Rufus')
>>> dd['dogs'].append('Kathrin')
>>> dd['dogs'].append('Mr Sniffles')

>>> dd['dogs']
['Rufus', 'Kathrin', 'Mr Sniffles']
‫‪  176‬ترفندهای پایتون‬

‫‪ - collections.ChainMap .4-1-5‬جستجوی چنـدین دیکشـنری در یـک‬


‫‪mapping‬‬
‫این نرو داده مغتوانرد چنردین دیاشرنری را در یرک نگاشرت ذخمرره پنرد‪ .‬بردین‬
‫صورت برای یافتن یک پلمد در هرپدام از دیاشنریها‪ ،‬مغتوانمرد بره سرادگغ از ایرن‬
‫نگاشت استفاده پنمد‪.‬‬

‫>>>‬ ‫‪from collections import ChainMap‬‬


‫>>>‬ ‫}‪dict1 = {'one': 1, 'two': 2‬‬
‫>>>‬ ‫}‪dict2 = {'three': 3, 'four': 4‬‬
‫>>>‬ ‫)‪chain = ChainMap(dict1, dict2‬‬

‫‪>>> chain‬‬
‫‪ChainMap({'one': 1, 'two': 2}, {'three': 3, 'four':‬‬
‫)}‪4‬‬

‫‪# ChainMap searches each collection in the chain‬‬


‫‪# from left to right until it finds the key (or‬‬
‫‪fails):‬‬
‫]'‪>>> chain['three‬‬
‫‪3‬‬
‫]'‪>>> chain['one‬‬
‫‪1‬‬
‫]'‪>>> chain['missing‬‬
‫'‪KeyError: 'missing‬‬

‫‪ - types.MappingProxyType .5-1-5‬ســـاخت دیکشـــنریهای فقـــط‬


‫خواندنی‬
‫از این نو داده برای ایزاد دیاشنریهایغ به صورت فقط خواندنغ استفاده مغشود‪.‬‬
‫به بمانغ دیگر مغتوان دیاشنریهایغ تغممر ناپذیر (‪ )immutable‬ایزاد نمود‪.‬‬
‫برای مثال‪ ،‬در صورتغ په قصد دارید در یک پالس یا متدی از برنامهی خود یرک‬
‫دیاشررنری را برگردانمررد و از تغممررر آن در سررایر قسررمتهای برنامرره جلرروگمری پنمررد‪،‬‬
‫مغتوانمد با استفاده از ‪ MappingProxyType‬این محدودیت را روی دیاشرنری اعمرال‬
‫نمایمد‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪177 ‬‬

‫‪>>> from types import MappingProxyType‬‬


‫}‪>>> writable = {'one': 1, 'two': 2‬‬
‫)‪>>> read_only = MappingProxyType(writable‬‬

‫‪# The proxy is read-only:‬‬


‫]'‪>>> read_only['one‬‬
‫‪1‬‬
‫‪>>> read_only['one'] = 23‬‬
‫‪TypeError:‬‬
‫‪"'mappingproxy' object does not support item‬‬
‫"‪assignment‬‬

‫‪# Updates to the original are reflected in the proxy:‬‬


‫‪>>> writable['one'] = 42‬‬
‫‪>>> read_only‬‬
‫)}‪mappingproxy({'one': 42, 'two': 2‬‬

‫‪ .6-1-5‬نکات کلیدی در هنگام کار با دیکشنریها‬


‫تمام انوا دیاشنریهایغ په در این بخی به توضمح دربارهی آنها پرداختمم‪ ،‬جرزء‬
‫پتابخانههای استاندارد در زبان پایتون هستند‪.‬‬
‫اما با تمام این توضمحات‪ ،‬ما به شما پمشرنهاد مرغپنمم پره در برنامرهی خرود از نرو‬
‫دادهی ‪ dict‬په همان دیاشنری اصلغ در پایتون است استفاده پنمد‪ .‬این دیاشنری برای‬
‫همهی افرادی په در آینده پد شما را مغخوانند قابل فهم بوده و همچنمن اپثر نمازهرای‬
‫شما را برطرف مغپند‪.‬‬
‫یا پمچمده دارید از‬ ‫بهتر است تنها در صورتغ په در برنامهی خود نماز به پاری خا‬
‫سایر دیاشنریهایغ په در این بخی نام برده شده است استفاده پنمد‪.‬‬

‫‪ .2-5‬انواع آرایههای پایتونی‬


‫آرایهها‪ 1‬یاغ از پایهای ترین نو داده در تمام زبانهرای برنامره نویسرغ هسرتند و از‬
‫آنها به صورت گسترده در بسماری از برنامهها و الگوریتمها استفاده مغشود‪.‬‬

‫‪1 Array‬‬
‫‪  178‬ترفندهای پایتون‬

‫در این بخی نگاهغ به پماده سازی آرایهها در پایتون مغانردازیم و پتابخانرههایغ را‬
‫در زبان پایتون بررسغ مغپنمم په آرایهها را به نحوی دیگر در ایرن زبران پمراده سرازی‬
‫نمودهاند‪ .‬بنابراین شما با نقاط قوت و ضرعف هرپردام از آنهرا آشرنا شرده و مغتوانمرد‬
‫تصممم بگمرید په از پدام یک در برنامههای خود استفاده پنمد‪ .‬اما بهتر اسرت ابتردا بره‬
‫توضمحغ دربارهی ماهمت آرایهها بپردازیم‪.‬‬
‫آرایهها از تعداد ابتغ از مقادیر تشامل شدهاند په به هر پدام از این مقادیر مغتوان‬
‫با استفاده از اندیس آن دسترسغ داشت‪ .‬از آنزا په آرایهها اطالعات را در بلوکهرایغ‬
‫همزرروار در حافظرره (‪ )memory‬ذخمررره مغپننررد‪ ،‬آنهررا را برره عنرروان سرراختار دادهی‬
‫همزوار در نظر مغگمرند‪.‬‬
‫یک مثال واقعغ برای آرایهها یک پارپمنگ است‪ :‬مغتوان بره پارپمنرگ بره عنروان‬
‫یک آبزات نگاه پرد‪ .‬در پارپمنگ نقاطغ وجود دارد په با شمارهای منحصر بره فررد‬
‫وسایل نقلمه بوده په ممان است خرالغ یرا دارای‬ ‫مشخص شدهاند‪ .‬این نقاط‪ ،‬مخصو‬
‫یک ماشمن یا موترور باشرند‪ .‬امرا تمرام پارپمنگهرا بره یرک صرورت نمسرتند‪ .‬برخرغ از‬
‫نو خاصغ از وسایل نقلمه هستند‪ .‬این نو از پارپمنگهرا را‬ ‫پارپمنگها تنها مخصو‬
‫مغتوان با آرایههایغ مقایسه پرد په تنها یک نو داده‪ 1‬را در خود جای دادهاند‪.‬‬
‫از لحاظ عملارد‪ ،‬دسترسغ به عناصر یک آرایه با استفاده از اندیس آن بسمار سرریع‬
‫بوده و هزینهای برابر با )‪ O(1‬دارد‪.‬‬
‫آرایهها در پایتون و پتابخانههای اسرتاندارد آن بره گونرههای مختلفرغ پمراده سرازی‬
‫شدهاند په در ادامه نگاهغ به انوا آنها خواهمم انداخت‪.‬‬

‫‪2‬‬
‫‪ - list .1-2-5‬آرایههای تغییرپذیر‬
‫لمست در پایتون جزئغ از هستهی اصلغ این زبان است‪ .‬علغررم نام آن‪ ،‬از لمست در‬
‫پایتون به عنوان آرایههایغ تغممر پذیر استفاده مغشود‪.‬‬

‫‪1 typed array‬‬


‫‪2 mutable‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪179 ‬‬

‫این بدان معناست په مغتوان به یک لمست در پرایتون‪ ،‬عناصرری را اضرافه یرا از آن‬
‫حذف نمود و این لمست در پشت صحنه به طور خودپار فضای حافظرهای را بررای ایرن‬
‫مغدهد یا فضای حافظهای را در هنگام حذف یک عنصرر آزاد‬ ‫عناصر جدید اختصا‬
‫مغپند‪.‬‬
‫یک لمست در پایتون مغتواند مقردار دلخرواهغ از عناصرر را در خرود نگره دارد‪ .‬در‬
‫پایتون همه چمز به عنوان آبزات در نظر گرفته مغشرود‪ .‬بنرابراین شرما مغتوانمرد یرک‬
‫لمست بسازید په شامل انوا مختلفغ از دادهها است‪ .‬شاید بتوان به این قابلمت به عنروان‬
‫ناتهی قوت لمستها نگاه پرد؛ اما در واقع با این پار‪ ،‬یک لمست مزبرور اسرت فضرای‬
‫بمشتری از حافظه را اشغال پند‪.‬‬

‫]'‪>>> arr = ['one', 'two', 'three‬‬


‫]‪>>> arr[0‬‬
‫'‪'one‬‬

‫‪# Lists have a nice repr:‬‬


‫‪>>> arr‬‬
‫]'‪['one', 'two', 'three‬‬

‫‪# Lists are mutable:‬‬


‫'‪>>> arr[1] = 'hello‬‬
‫‪>>> arr‬‬
‫]'‪['one', 'hello', 'three‬‬

‫]‪>>> del arr[1‬‬


‫‪>>> arr‬‬
‫]'‪['one', 'three‬‬

‫‪# Lists can hold arbitrary data types:‬‬


‫)‪>>> arr.append(23‬‬
‫‪>>> arr‬‬
‫]‪['one', 'three', 23‬‬
‫‪  180‬ترفندهای پایتون‬

‫‪1‬‬
‫‪ - tuple .2-2-5‬آرایههای تغییر ناپذیر‬
‫همانند لمستها‪ ،‬تاپلها نمز جزء هستهی اصلغ زبان پایتون هستند‪ .‬اما برخالف لمست‪،‬‬
‫تاپلها تغممر ناپذیرند‪ .‬بدین معنغ په نمغتوان عنصری را از یک تاپرل حرذف یرا بره آن‬
‫اضافه نمود‪ .‬تمام عناصر موجود در تاپل باید در هنگام ساخت آن تعریف شوند‪.‬‬
‫تاپلها نمز مغتوانند انوا مختلفرغ از دادههرا را درون خرود نگره دارنرد‪ .‬امرا هماننرد‬
‫لمستها‪ ،‬این عمل باعث مغشود تا یک تاپل فضای حافظهی بمشتری اشغال پند‪.‬‬

‫'‪>>> arr = 'one', 'two', 'three‬‬


‫]‪>>> arr[0‬‬
‫'‪'one‬‬

‫‪# Tuples have a nice repr:‬‬


‫‪>>> arr‬‬
‫)'‪('one', 'two', 'three‬‬

‫‪# Tuples are immutable:‬‬


‫'‪>>> arr[1] = 'hello‬‬
‫‪TypeError:‬‬
‫"‪"'tuple' object does not support item assignment‬‬

‫]‪>>> del arr[1‬‬


‫‪TypeError:‬‬
‫"‪"'tuple' object doesn't support item deletion‬‬

‫‪# Tuples can hold arbitrary data types:‬‬


‫)‪# (Adding elements creates a copy of the tuple‬‬
‫)‪>>> arr + (23,‬‬
‫)‪('one', 'two', 'three', 23‬‬

‫‪ - array.array .3-2-5‬آرایههایی با ساختار پایهای‬


‫آرایههایغ په از پالس ‪ array.array‬در پایتون ساخته مغشوند‪ ،‬تغممر پرذیر بروده و‬
‫رفتاری مشابه با لمست در پایتون دارند‪ .‬اما تفاوت اصلغ این نو از آرایهها با لمستها در‬

‫‪1 immutable‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪181 ‬‬

‫پایتون در ذخمره سازی نو دادههرا اسرت‪ .‬بردین معنرا پره در ایرن نرو از آرایرهها تنهرا‬
‫مغتوان عناصری از یک نو داده ذخمره نمود‪.‬‬
‫به دلمل این محدودیت موجود در ‪ ،array.array‬این نو از آرایهها فضرای پمترری‬
‫را در مقایسه با لمست یا تاپل در حافظه اشغال مغپنند‪.‬‬
‫همچنمن آرایهها‪ ،‬از متردهایغ مشرابه برا لمسرت نمرز پشرتمبانغ مغپننرد‪ .‬بنرابراین شرما‬
‫مغتوانمد از این آرایهها به عنوان جایگزینغ برای لمسرتها در برنامرههای خرود اسرتفاده‬
‫پنمد؛ بدون ایناه نمازی به تغممر اساسغ در سایر قسمتهای برنامهی خود داشته باشمد‪.‬‬

‫‪>>> import array‬‬


‫))‪>>> arr = array.array('f', (1.0, 1.5, 2.0, 2.5‬‬
‫]‪>>> arr[1‬‬
‫‪1.5‬‬

‫‪# Arrays have a nice repr:‬‬


‫‪>>> arr‬‬
‫)]‪array('f', [1.0, 1.5, 2.0, 2.5‬‬

‫‪# Arrays are mutable:‬‬


‫‪>>> arr[1] = 23.0‬‬
‫‪>>> arr‬‬
‫)]‪array('f', [1.0, 23.0, 2.0, 2.5‬‬

‫]‪>>> del arr[1‬‬


‫‪>>> arr‬‬
‫)]‪array('f', [1.0, 2.0, 2.5‬‬

‫)‪>>> arr.append(42.0‬‬
‫‪>>> arr‬‬
‫)]‪array('f', [1.0, 2.0, 2.5, 42.0‬‬

‫‪# Arrays are "typed":‬‬


‫'‪>>> arr[1] = 'hello‬‬
‫"‪TypeError: "must be real number, not str‬‬
‫‪  182‬ترفندهای پایتون‬

‫‪ - str .4-2-5‬آرایههایی تغییر ناپذیر از کاراکترها‬


‫پایتون از آبزاتهای ‪ str‬به منظور ذخمره سرازی دنبالرهای از پاراپترهرای یونماردِ‬
‫تغممر ناپذیر استفاده مغپند‪ .‬یعنغ ‪ str‬را مغتوان به عنوان آرایهای از پاراپترهرا در نظرر‬
‫گرفت‪ .‬به بمان دیگر هر پاراپتر در رشته نمز خود یک آبزات ‪ str‬به طول ‪ 1‬است‪.‬‬
‫از آنزا په رشتهها در پایتون از نو دادهی تغممر ناپذیر هستند‪ ،‬به منظرور تغممرر یرک‬
‫رشته باید یک پپغ از آن ایزاد پرده و آن را به عنوان متغمری جدیرد در نظرر گرفرت‪.‬‬
‫نزدیک ترین معادل برای عنوان «رشتهی قابل تغممر»‪ ،‬ذخمرهی هر پاراپتر از یک رشرته‬
‫به عنوان عناصری در یک لمست است‪.‬‬

‫'‪>>> arr = 'abcd‬‬


‫]‪>>> arr[1‬‬
‫'‪'b‬‬

‫‪>>> arr‬‬
‫'‪'abcd‬‬

‫‪# Strings are immutable:‬‬


‫'‪>>> arr[1] = 'e‬‬
‫‪TypeError:‬‬
‫"‪"'str' object does not support item assignment‬‬

‫]‪>>> del arr[1‬‬


‫‪TypeError:‬‬
‫"‪"'str' object doesn't support item deletion‬‬

‫‪# Strings can be unpacked into a list to‬‬


‫‪# get a mutable representation:‬‬
‫)'‪>>> list('abcd‬‬
‫]'‪['a', 'b', 'c', 'd‬‬
‫))'‪>>> ''.join(list('abcd‬‬
‫'‪'abcd‬‬

‫‪# Strings are recursive data structures:‬‬


‫)'‪>>> type('abc‬‬
‫">'‪"<class 'str‬‬
‫)]‪>>> type('abc'[0‬‬
‫">'‪"<class 'str‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪183 ‬‬

‫‪ - bytes .5-2-5‬آرایههایی تغییر ناپذیر از بایتها‬


‫آبزاتهایغ از بایت‪ ،‬دنبالهای تغممر ناپرذیر متشرال از تعرداد زیرادی بایرت (اعرداد‬
‫صحمح در بازهی ‪ 0‬تا ‪ )255‬هستند‪ .‬از لحاظ مفهومغ‪ ،‬آنها همانند آبزارت ‪ str‬هسرتند‬
‫په تنها بزای پاراپتر شامل بایت مغباشند‪.‬‬
‫بره خرود آن‬ ‫برای ساخت آبزاتهایغ از نو بایرت‪ ،‬بایرد از سمنتاسرغ مخصرو‬
‫استفاده نمود‪ .‬البته نوعغ دیگرر از ایرن آرایرهها برا نرام ‪ bytearray‬نمرز وجرود دارد پره‬
‫برخالف ‪ bytes‬و ‪ str‬تغممر پذیر هستند‪ .‬در بخی آینده بمشتر برا ایرن نمونره از آرایرهها‬
‫آشنا خواهمم شد‪.‬‬

‫))‪>>> arr = bytes((0, 1, 2, 3‬‬


‫]‪>>> arr[1‬‬
‫‪1‬‬

‫‪# Bytes literals have their own syntax:‬‬


‫‪>>> arr‬‬
‫'‪b'\x00\x01\x02\x03‬‬
‫'‪>>> arr = b'\x00\x01\x02\x03‬‬

‫‪# Only valid "bytes" are allowed:‬‬


‫))‪>>> bytes((0, 300‬‬
‫")‪ValueError: "bytes must be in range(0, 256‬‬

‫‪# Bytes are immutable:‬‬


‫‪>>> arr[1] = 23‬‬
‫‪TypeError:‬‬
‫"‪"'bytes' object does not support item assignment‬‬

‫]‪>>> del arr[1‬‬


‫‪TypeError:‬‬
‫"‪"'bytes' object doesn't support item deletion‬‬
‫‪  184‬ترفندهای پایتون‬

‫‪ - bytearray .6-2-5‬آرایههایی تغییرپذیر از بایتها‬


‫این نو از آرایهها متشال از بایتها (اعداد صرحمح در برازهی ‪ 0‬ترا ‪ )255‬بروده پره‬
‫تغممر پذیر هستند‪bytearray .‬ها بسمار شبمه به ‪ bytes‬در بخی قبل هستند با این تفراوت‬
‫په شما مغتوانمد این نمونه از آرایهها را به راحترغ تغممرر دهمرد (عنصرری را حرذف یرا‬
‫اضافه نمایمد)‪.‬‬
‫همچنمن این نو از آرایهها را مغتوان به آرایههای تغممر ناپذیر ‪ bytes‬تبردیل نمرود‪.‬‬
‫اما توجه داشته باشمد په این عمل به صرورت پپرغ پرردن تمرام محتویرات ‪bytearray‬‬
‫انزام مغگمرد په عملغ بسمار پند با هزینهی )‪ O(n‬است‪.‬‬
185  ‫ ساختارهای داده در پایتون‬:‫فصل پنجم‬

>>> arr = bytearray((0, 1, 2, 3))


>>> arr[1]
1

# The bytearray repr:


>>> arr
bytearray(b'\x00\x01\x02\x03')

# Bytearrays are mutable:


>>> arr[1] = 23
>>> arr
bytearray(b'\x00\x17\x02\x03')

>>> arr[1]
23

# Bytearrays can grow and shrink in size:


>>> del arr[1]
>>> arr
bytearray(b'\x00\x02\x03')

>>> arr.append(42)
>>> arr
bytearray(b'\x00\x02\x03*')

# Bytearrays can only hold "bytes"


# (integers in the range 0 <= x <= 255)
>>> arr[1] = 'hello'
TypeError: "an integer is required"

>>> arr[1] = 300


ValueError: "byte must be in range(0, 256)"

# Bytearrays can be converted back into bytes


objects:
# (This will copy the data)
>>> bytes(arr)
b'\x00\x02\x03*'
‫‪  186‬ترفندهای پایتون‬

‫‪ .7-2-5‬نکات کلیدی در هنگام کار با آرایهها در پایتون‬


‫شما مغتوانمد از انوا مختلفغ از آرایههایغ په در این بخی نام برده شدهاند بسته به‬
‫نماز خود در برنامههایتان اسرتفاده پنمرد‪ .‬پتابخانرههای دیگرری بره رمرر از پتابخانرههای‬
‫استاندارد پایتون به منظور پار با آرایهها نمز وجود دارد‪ .‬برای مثال ‪ ،NumPy‬پتابخانهای‬
‫بسمار قدرتمند برای پار با دادههرا در زممنرهی داده پراوی و علروم پرامپموتر اسرت پره‬
‫مغتوانمد از آن نمز استفاده نمایمد‪.‬‬
‫پمشنهاد مغشود تا قبل از شرو به پار با آرایهها در برنامهی خود به ناات زیر توجه‬
‫پنمد‪:‬‬
‫‪ ‬در صورتغ په قصد دارید دادههایغ برا نرو مختلرف را در یرک آرایره ذخمرره‬
‫پنمد‪ ،‬بهتر است از ‪ list‬یا ‪ tuple‬در پایتون استفاده پنمد‪.‬‬
‫‪ ‬در صورتغ په تنها دادههایغ به صورت اعداد صحمح یا اعشاری دارید و سرعت‬
‫عملارد پار با آرایهها برای شما نمز مهم است‪ ،‬بهتر است ترا از ‪ array.array‬در‬
‫برنامه خود اسرتفاده پنمرد‪ .‬همچنرمن اسرتفاده از پتابخانرههایغ ماننرد ‪ NumPy‬و‬
‫‪ Pandas‬نمز برای پار با این نو از دادهها پمشنهاد مغشود‪.‬‬
‫‪ ‬در صورتغ په قصد دارید با دادههایغ به صورت متن پار پنمرد‪ ،‬بهترر اسرت ترا‬
‫آنها را در متغمری به صورت ‪ str‬ذخمره پنمد و یا اگر قصد دارید ترا محتویرات‬
‫متن خود را تغممرر دهمرد‪ ،‬در برنامرهی خرود مغتوانمرد ایرن متغمرر را بره صرورت‬
‫پاراپترهایغ جداگانه در یک لمست ذخمره نمایمد‪.‬‬
‫‪ ‬و اگر قصد دارید دادههایغ را به صرورت بایرت ذخمرره پنمرد‪ ،‬بهترر اسرت ترا از‬
‫‪ bytes‬یا ‪ bytearray‬استفاده نمایمد‪.‬‬
‫به طور پلغ در بسماری از موارد پمشنهاد مغشود تا از ‪ list‬در برنامهی خرود اسرتفاده‬
‫پنمد و در صورتغ په در آینده نماز به بهمنره سرازی پرد خرود داشرتمد‪ ،‬مغتوانمرد آن را‬
‫تغممر دهمد‪ .‬چرا په استفاده از ‪list‬ها ساده تر بوده و همچنمن سایر برنامه نویسان نمز با این‬
‫نو داده آشنایغ پامل دارند‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪187 ‬‬

‫‪1‬‬
‫‪struct .3-5‬ها و اشیاء انتقال داده‬
‫در مقایسه با آرایهها‪ ،‬ساختارهای دادهی رپورد دارای مقداری فملد ابت هستند پره‬
‫هر فملد مغتواند یک نام داشته و نو دادههای آن ها با یادیگر تفاوت داشته باشد‪.‬‬
‫در این بخی‪ ،‬با نحوهی پماده سازی رپوردها و ‪struct‬ها در پایتون آشرنا مغشرویم‪.‬‬
‫برای این پار تنها از پتابخانههای استاندارد پایتون استفاده خواهمم نمود‪.‬‬
‫پایتون انوا دادههای مختلفغ را برای ما فراهم آورده است په به مرا ایرن قابلمرت را‬
‫مغدهد تا بتوانمم رپوردها‪struct ،‬ها و آبزاتهای انتقال داده را پمراده سرازی نمرایمم‪.‬‬
‫در این بخی یک نگاه پلغ به تمام آنها و ویژگغهای هریک خواهمم انداخت‪.‬‬

‫‪ - dict .1-3-5‬آبجکتهای داده ساده‬


‫دیاشنریها در پایتون مغتواننرد تعرداد دلخرواهغ از آبزاتهرا را در خرود ذخمرره‬
‫نمایند په هر پدام با یک پلمد مشخص مغشوند‪ .‬دیاشنریها را با نامهایغ چون ‪map‬‬
‫و آرایههای انزمنغ نمز مغشناسند‪ .‬از قابلمتهای خوب دیاشنری مغتروان بره جسرت و‬
‫جو در آن با استفاده از یک پلمد با سرعت باال اشاره نمود‪.‬‬
‫از دیاشنریها در پایتون مغتوان به عنوان نرو دادهی رپرورد یرا آبزارت اسرتفاده‬
‫نمود‪ .‬همچنمن نحوهی ساخت دیاشنریها نمز در پرایتون بسرمار سراده و مختصرر اسرت‪.‬‬
‫آبزاتهایغ په به صورت دیاشنری ساخته مغشروند قابرل تغممرر هسرتند و بره راحترغ‬
‫مغتوان فملدهایغ را به یک آبزات اضافه نمود و یا از آن حذف پرد‪.‬‬

‫‪1 Data Transfer Objects‬‬


‫ ترفندهای پایتون‬ 188

car1 = {
'color': 'red',
'mileage': 3812.4,
'automatic': True,
}
car2 = {
'color': 'blue',
'mileage': 40231,
'automatic': False,
}

# Dicts have a nice repr:


>>> car2
{'color': 'blue', 'automatic': False, 'mileage':
40231}

# Get mileage:
>>> car2['mileage']
40231

# Dicts are mutable:


>>> car2['mileage'] = 12
>>> car2['windshield'] = 'broken'
>>> car2
{'windshield': 'broken', 'color': 'blue',
'automatic': False, 'mileage': 12}

# No protection against wrong field names,


# or missing/extra fields:
car3 = {
'colr': 'green',
'automatic': False,
'windshield': 'broken',
}

‫ گروهی از آبجکتهای تغییر ناپذیر‬- tuple .2-3-5


.‫از تاپلها در پایتون مغتوان برای گروه بندی تعداد دلخواهغ آبزات استفاده نمود‬
‫توجه داشته باشمد په این نو داده تغممر ناپذیر است و پس از ساخت یک تاپل نمغتوان‬
.‫آن را تغممر داد‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪189 ‬‬

‫از لحاظ عملارد‪ ،‬تاپلها از فضای حافظهی پمتری نسبت به ‪list‬ها در پایتون استفاده‬
‫پرده و همچنمن سریع تر نمز هستند‪.‬‬
‫‪1‬‬
‫همانطور په در پد زیر مشاهده مغپنمد‪ ،‬برای ساخت یک تاپل از تنها یک آپارد‬
‫‪ LOAD_CONST‬استفاده شده است‪ .‬در صورتغ په برای سراخت یرک لمسرت برا همران‬
‫محتویات به عملمات بمشتری نماز است‪.‬‬

‫‪>>> import dis‬‬


‫‪>>> dis.dis(compile("(23, 'a', 'b', 'c')", '',‬‬
‫))'‪'eval‬‬
‫‪0 LOAD_CONST‬‬ ‫‪4 ((23, 'a', 'b',‬‬
‫))'‪'c‬‬
‫‪3 RETURN_VALUE‬‬

‫‪>>> dis.dis(compile("[23, 'a', 'b', 'c']", '',‬‬


‫))'‪'eval‬‬
‫‪0 LOAD_CONST‬‬ ‫)‪0 (23‬‬
‫‪3 LOAD_CONST‬‬ ‫)'‪1 ('a‬‬
‫‪6 LOAD_CONST‬‬ ‫)'‪2 ('b‬‬
‫‪9 LOAD_CONST‬‬ ‫)'‪3 ('c‬‬
‫‪12 BUILD_LIST‬‬ ‫‪4‬‬
‫‪15 RETURN_VALUE‬‬

‫البته شما نبایرد بررای تفراوت برمن ایرن دو اهممرت زیرادی قائرل شروید‪ .‬در برخرغ از‬
‫پروژهها تفاوت عملارد بمن این دو بسمار ناچمز است‪.‬‬
‫شاید بتوان گفت یاغ از نقاط ضعف در تاپلها‪ ،‬توانرایغ دسترسرغ بره فملردهای آن‬
‫تنها با استفاده از شمارهی اندیس آن فملد اسرت‪ .‬شرما نمغتوانمرد هماننرد دیاشرنری بره‬
‫دهمد‪ .‬شاید بتوان گفت این عمل خوانایغ پد شما‬ ‫فملدهایتان در تاپل یک نام اختصا‬
‫را پمغ تحت تا مر قرار خواهد داد‪.‬‬
‫همچنمن گاهغ اوقات پمغ سخت است په بتوان اطممنان حاصل نمود پره دو تاپرل‬
‫دارای تعداد فملد برابر بوده و ویژگغهای ذخمره شده در آن دو به یرک صرورت اسرت‪.‬‬

‫‪1 opcode‬‬
‫‪  190‬ترفندهای پایتون‬

‫بنابراین پمشنهاد مغشود ترا در صرورت اماران فملردهای پمترری در یرک تاپرل ذخمرره‬
‫نمایمد‪.‬‬

‫‪# Fields: color, mileage, automatic‬‬


‫)‪>>> car1 = ('red', 3812.4, True‬‬
‫)‪>>> car2 = ('blue', 40231.0, False‬‬

‫‪# Tuple instances have a nice repr:‬‬


‫‪>>> car1‬‬
‫)‪('red', 3812.4, True‬‬
‫‪>>> car2‬‬
‫)‪('blue', 40231.0, False‬‬

‫‪# Get mileage:‬‬


‫]‪>>> car2[1‬‬
‫‪40231.0‬‬

‫‪# Tuples are immutable:‬‬


‫‪>>> car2[1] = 12‬‬
‫‪TypeError:‬‬
‫"‪"'tuple' object does not support item assignment‬‬

‫‪# No protection against missing/extra fields‬‬


‫‪# or a wrong order:‬‬
‫)'‪>>> car3 = (3431.5, 'green', True, 'silver‬‬

‫‪ .3-3-5‬نوشتن یک کالس ‪ -‬کنترل بهتر و دقیقتر‬


‫با ساخت یک پالس در پایتون نمز مغتوانمم اشما دادهای بسازیم و مطمئن باشمم پره‬
‫هر آبزات ایزاد شده از این پالس‪ ،‬فملدهای یاسانغ دارد‪.‬‬
‫سرراخت چنررمن پالسهررایغ پررار سررادهای اسررت‪ .‬امررا برره منظررور پمرراده سررازی سررایر‬
‫ویژگغهای مربوط به یک آبزات نمازمند یک سری اعمرال اضرافه اسرت‪ .‬بررای مثرال‬
‫برای ایزاد فملدهایغ جدید در یک پالس در هنگرام سراخت یرک شرغء‪ ،‬بایرد از مترد‬
‫__‪ __init‬استفاده نمود‪ .‬همچنمن مغتروان از مترد __‪ __repr‬نمرز در یرک پرالس بره‬
‫منظور شخصغ سازی نمایی آبزاتهای پالس استفاده پرد‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪191 ‬‬

‫فملدهای ذخمره شده در یک پالس قابل تغممر هستند و همچنمن بره راحترغ مغتروان‬
‫دپوریترور‪1‬‬ ‫فملدهای جدیدی به یک پالس اضافه نمود‪ .‬البته شما مغتوانمد با استفاده از‬
‫‪ property‬فملدهایغ فقط خواندنغ نمز ایزاد پنمد‪.‬‬
‫نوشتن یک پالس این قابلمرت را بره مرا مغدهرد ترا بتروانمم متردهای مختلفرغ بررای‬
‫آبزاتهای یک پالس تعریف پرده و رفتار دادههای خود را شخصغ سازی پنمم‪.‬‬

‫‪class Car:‬‬
‫‪def __init__(self, color, mileage, automatic):‬‬
‫‪self.color = color‬‬
‫‪self.mileage = mileage‬‬
‫‪self.automatic = automatic‬‬

‫)‪>>> car1 = Car('red', 3812.4, True‬‬


‫)‪>>> car2 = Car('blue', 40231.0, False‬‬

‫‪# Get the mileage:‬‬


‫‪>>> car2.mileage‬‬
‫‪40231.0‬‬

‫‪# Classes are mutable:‬‬


‫‪>>> car2.mileage = 12‬‬
‫'‪>>> car2.windshield = 'broken‬‬

‫‪# String representation is not very useful‬‬


‫‪# (must add a manually written __repr__ method):‬‬
‫‪>>> car1‬‬
‫>‪<Car object at 0x1081e69e8‬‬

‫‪ - collections.namedtuple .4-3-5‬اشیا دادهای ساده‬


‫این پالس په در نسخهی پایتون ‪ 2.6‬به بعد قابل استفاده است‪ ،‬زیرساخت آن همران‬
‫نررو دادهی ‪ tuple‬اسررت‪ .‬هماننررد زمررانغ پرره یررک پررالس تعریررف مررغپنمم‪ ،‬از‬
‫‪ namedtuple‬نمز مغتوان به منظور ایزراد فملردهایغ یاسران بررای آبزاتهرای خرود‬
‫استفاده پنمم‪.‬‬

‫‪1 Decorator‬‬
‫‪  192‬ترفندهای پایتون‬

‫‪namedtuple‬ها همانند ‪tuple‬ها تغممر ناپذیر هستند‪ .‬این بدان معناسرت پره پرس از‬
‫ساخت یک نمونه از ‪ ،namedtuple‬شما نمغتوانمرد فملردهایغ را بره آن اضرافه پررده‪،‬‬
‫تغممر دهمد و یا از آن حذف نمایمد‪.‬‬
‫همررانطور پرره از اسررم ‪ namedtuple‬مشررخص اسررت‪ ،‬برره هررر عنصررر از یررک‬
‫‪ namedtuple‬مغتوان به وسملهی نام آن دسترسغ پمدا پررد‪ .‬طبمعترا دسترسرغ بره یرک‬
‫عنصر با استفاده از نام آن‪ ،‬ساده تر از دسترسغ به آن با پمک عدد اندیس مربوط به آن‬
‫عنصر است‪.‬‬
‫‪ namedtuple‬به همان اندازهی ‪tuple‬ها حافظه اشغال مغپند پره مقردار آن بسرمار‬
‫پمتر از مقدار حافظهی اشغال شده توسط یک پالس در پایتون است‪.‬‬

‫‪>>> from collections import namedtuple‬‬


‫‪>>> from sys import getsizeof‬‬

‫)‪>>> p1 = namedtuple('Point', 'x y z')(1, 2, 3‬‬


‫)‪>>> p2 = (1, 2, 3‬‬

‫)‪>>> getsizeof(p1‬‬
‫‪72‬‬
‫)‪>>> getsizeof(p2‬‬
‫‪72‬‬

‫استفاده از ‪namedtuple‬ها به خوانایغ بمشتر پد شما پمک پرده و سراختار بهترری‬


‫به برنامهی شما مغدهد‪ .‬چراپه مغتوان با اسرتفاده از ایرن نرو داده‪ ،‬منظرور خرود را در‬
‫برنامه بهتر به برنامهنویسان دیگر منتقل پنمم‪.‬‬
193  ‫ ساختارهای داده در پایتون‬:‫فصل پنجم‬

>>> from collections import namedtuple

>>> Car = namedtuple('Car' , 'color mileage


automatic')
>>> car1 = Car('red', 3812.4, True)

# Instances have a nice repr:


>>> car1
Car(color='red', mileage=3812.4, automatic=True)

# Accessing fields:
>>> car1.mileage
3812.4

# Fields are immtuable:


>>> car1.mileage = 12
AttributeError: "can't set attribute"
>>> car1.windshield = 'broken'
AttributeError:
"'Car' object has no attribute 'windshield'"

‫ بهبود یافته‬NamedTuple ‫ نوع دادهی‬- typing.NamedTuple .5-3-5


‫ ایرن نرو داده‬.‫ به بعد مغتروان اسرتفاده پررد‬3/6 ‫ در پایتون نسخه‬NamedTuple ‫از‬
‫ اما تفاوت آن در نحوهی‬.‫ است په در بخی قبل معرفغ شد‬namedtuple ‫بسمار شبمه به‬
.‫تعریف یک تاپل و قابلمت افزودن راهنما برای نو فملدهای درون تاپل است‬
type ‫ نوشرتن‬،‫ را انزرام مغدهرد‬type checking ‫ په عملمرات‬mypy1 ‫بدون وجود‬
‫ها مغتواند‬type ‫ نوشتن‬mypy ‫ اما بدون ابزارهایغ نظمر‬.‫ها اجباری نمغباشد‬annotaion
.‫راهنمایغ برای سایر توسعه دهندگان باشد‬

1 mypy-lang.org
‫ ترفندهای پایتون‬ 194

>>> from typing import NamedTuple

class Car(NamedTuple):
color: str
mileage: float
automatic: bool

>>> car1 = Car('red', 3812.4, True)

# Instances have a nice repr:


>>> car1
Car(color='red', mileage=3812.4, automatic=True)

# Accessing fields:
>>> car1.mileage
3812.4

# Fields are immutable:


>>> car1.mileage = 12
AttributeError: "can't set attribute"
>>> car1.windshield = 'broken'
AttributeError:
"'Car' object has no attribute 'windshield'"

# Type annotations are not enforced without


# a separate type checking tool like mypy:
>>> Car('red', 'NOT_A_FLOAT', 99)
Car(color='red', mileage='NOT_A_FLOAT', automatic=99)

C ‫ ساختارهای مرتب شدهی زبان‬- struct.Struct .6-3-5

‫ مقدار ورودی دریافت شده از طرف برنامه نویس را به اشرماء‬struct.Struct ‫پالس‬


‫ با استفاده از این پالس مغتوان دادههای باینری در یرک‬.‫بایت در پایتون تبدیل مغپند‬
.‫فایل را مدیریت نمود‬
.‫برای تعریف نو فملدهای درون خود استفاده مغپنند‬ ‫ها از سمنتاسغ خا‬Struct
‫ نشران دهنردهی‬f ‫ و‬bool ‫ ? نشران دهنردهی دادهی‬،integer ‫ نشان دهنردهی‬i ‫برای مثال‬
.‫ است‬float
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪195 ‬‬

‫از ‪Struct‬ها در پایتون به ندرت به منظور نمایی اشما داده استفاده مغشود‪ .‬ایرن نرو‬
‫دادهها بمشتر به منظور روشغ برای تبادل فرمت دادهها بمن زبان پایتون و ‪ C‬در نظر گرفته‬
‫مغشوند‪.‬‬

‫‪>>> from struct import Struct‬‬


‫)'‪>>> MyStruct = Struct('i?f‬‬
‫)‪>>> data = MyStruct.pack(23, False, 42.0‬‬

‫‪# All you get is a blob of data:‬‬


‫‪>>> data‬‬
‫'‪b'\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00(B‬‬

‫‪# Data blobs can be unpacked again:‬‬


‫)‪>>> MyStruct.unpack(data‬‬
‫)‪(23, False, 42.0‬‬

‫‪ - types.SimpleNamespace .7-3-5‬دسترســی آســان بــه فیلــدهای یــک‬


‫آبجکت‬
‫این پالس په در پایتون نسخهی ‪ 3/3‬به بعد مغتوان استفاده نمود‪ ،‬این قابلمرت را بره‬
‫ما مغدهد په با استفاده از نام فملدها به عناصر یک آبزات دسترسغ پمدا پنمم‪.‬‬
‫‪ SimpleNamespace‬ها تمام پلمدهای دسترسغ به عناصر درون خرود را بره عنروان‬
‫‪attribute‬های خرود در نظرر مغگمرنرد‪ .‬ایرن بردین معناسرت پره شرما مغتوانمرد بررای‬
‫دسترسغ به یک فملد بزای اسرتفاده از ]’‪ obj[‘key‬از ‪ obj.key‬اسرتفاده پنمرد‪ .‬همچنرمن‬
‫این نو داده همانند دیاشنری قابل تغممر است و شرما مغتوانمرد عناصرر درون آن را بره‬
‫راحتغ تغممر دهمد‪.‬‬
‫‪  196‬ترفندهای پایتون‬

‫‪>>> from types import SimpleNamespace‬‬


‫‪>>> car1 = SimpleNamespace(color='red',‬‬
‫‪mileage=3812.4,‬‬
‫)‪automatic=True‬‬

‫‪# The default repr:‬‬


‫‪>>> car1‬‬
‫‪namespace(automatic=True, color='red',‬‬
‫)‪mileage=3812.4‬‬

‫‪# Instances support attribute access and are mutable:‬‬


‫‪>>> car1.mileage = 12‬‬
‫'‪>>> car1.windshield = 'broken‬‬
‫‪>>> del car1.automatic‬‬
‫‪>>> car1‬‬
‫‪namespace(color='red', mileage=12,‬‬
‫)'‪windshield='broken‬‬

‫‪ .8-3-5‬نکات کلیدی در هنگام کار با رکوردها در پایتون‬


‫حال باید تصممم بگمریم په از پدام یک از مروارد ذپرر شرده در ایرن بخری بررای‬
‫ذخمر ه سازی اشماء داده استفاده نمایمم‪ .‬همانطور په مشاهده نمودید‪ ،‬گزینرههای زیرادی‬
‫در زبان پایتون بدین منظور پماده سازی شدهاند‪.‬‬
‫‪ ‬در صورتغ په فملدهای شما برای ذخمره سازی پم است (‪ 2‬یا ‪ 3‬فملد)‪ ،‬پمشرنهاد‬
‫مغشود تا از تاپلها در پایتون استفاده پنمد‪.‬‬
‫‪ ‬در صورتغ په قصد دارید تا فملدهای شما تغممر ناپذیر باشند‪ ،‬پمشنهاد مغشود ترا‬
‫از تاپلهررا یررا ‪ collections.namedtuple‬یررا ‪ typing.NamedTuple‬اسررتفاده‬
‫پنمد‪.‬‬
‫‪ ‬در صورتغ په قصد دارید فملدهای شما هرپدام یک نام مشخص داشته باشرند‪،‬‬
‫پمشرررنهاد مغشرررود از ‪ collections.namedtuple‬یرررا ‪typing.NamedTuple‬‬
‫استفاده نمایمد‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪197 ‬‬

‫‪ ‬در صورتغ په قصد دارید تا همه چمز را بره صرورت سراده و خوانرا بررای تمرام‬
‫برنامه نویسان دیگر پماده سازی نمایمد‪ ،‬بهتر اسرت ترا از دیاشرنریها در پرایتون‬
‫استفاده پنمد‪.‬‬
‫‪ ‬در صورتغ په قصد دارید متدهایغ را برای اشماء دادهی خرود در نظرر بگمریرد‪،‬‬
‫بهترررر اسرررت یرررک پرررالس بسرررازید یرررا از ‪ collections.namedtuple‬یرررا‬
‫‪ typing.NamedTuple‬برای ساخت پالس خود ارث بری پنمد‪.‬‬
‫‪ ‬در صورتغ په قصد دارید اشماء دادهی خود را در سطح یک شباه انتقال دهمرد‬
‫یا آنها را در دیسک ذخمره نمایمد‪ ،‬پمشنهاد مغشود از ‪ struct.Struct‬برای این‬
‫پار استفاده نمایمد‪.‬‬
‫به طور پلغ در صورتغ په به دنبال یک راه هممشرگغ و سراده بررای ذخمرره سرازی‬
‫اشرررماء داده هسرررتمد‪ ،‬بهترررر اسرررت از ‪ collections.namedtuple‬در پرررایتون ‪ 2‬و از‬
‫‪ typing.NamedTuple‬در پایتون ‪ 3‬استفاده پنمد‪.‬‬

‫‪ .4-5‬مجموعهها در پایتون‬
‫در این بخی در مورد نحوهی پماده سازی مزموعههای تغممر پذیر و تغممر ناپرذیر در‬
‫پایتون صحبت خواهمم نمود‪ .‬بهتر است ابتدا در مورد مزموعهها در پایتون بمشتر بدانمم‪:‬‬
‫یک مزموعه (‪ ،)set‬اجتماعغ از اشماء بدون ترتمب در یک سراختار داده اسرت پره‬
‫نمغتوان عناصری تاراری درون آن قرار داد‪ .‬معموال از مزموعهها در پایتون به منظرور‬
‫تست وجود یا عدم وجود یک عنصر در یک ساختار داده استفاده مغشود‪ .‬البته الزم بره‬
‫ذپر است په مغتوان عناصری نمز به یک مزموعه اضافه یا از آن حذف نمود‪ .‬همچنمن‬
‫مغتوان اجتما یا اشتراک دو مزموعه را با استفاده از این ساختار داده محاسبه نمود‪.‬‬
‫هزینهی چک پردن عضویت یرک عنصرر در مزموعرهای در پرایتون برابرر برا )‪O(1‬‬
‫است‪ .‬عملمات اتحاد یا اجتمرا دو مزموعره و همچنرمن تفراوت دو مزموعره و بدسرت‬
‫در پایتون به طور ممانگمن هزینهای برابر با )‪ O(n‬دارد‪.‬‬ ‫آوردن زیر مزموعهای خا‬
‫‪  198‬ترفندهای پایتون‬

‫ساخت یک مزموعه در پایتون بسمار ساده است‪.‬‬

‫}'‪vowels = {'a', 'e', 'i', 'o', 'u‬‬


‫})‪squares = {x * x for x in range(10‬‬

‫توجه داشته باشمد په به منظور ساخت یک مزموعهی خالغ در پایتون باید از دستور‬
‫)(‪ set‬استفاده پنمد‪ .‬از {} برای ایزاد یک دیاشنری خالغ در پایتون استفاده مغشود‪.‬‬
‫پتابخانههای استاندارد موجود در پایتون انوا مختلفغ از این نو داده را پماده سازی‬
‫پردهاند په در ادامه نگاهغ به آنها مغاندازیم‪.‬‬

‫‪ - set .1-4-5‬مجموعههای ساده در پایتون‬


‫این نو از مزموعهها‪ ،‬به صورت پمی فرض در پرایتون وجرود داشرته و مغتروان از‬
‫آنها استفاده نمود‪ .‬این نو داده قابل تغممر بروده و بره راحترغ مغتروان عناصرری بره آن‬
‫اضافه یا از آن حذف نمود‪ .‬مزموعهها در پایتون در واقع همان ویژگغهرای عملارردی‬
‫دیاشنریها را به همراه دارند‪.‬‬

‫}'‪>>> vowels = {'a', 'e', 'i', 'o', 'u‬‬


‫‪>>> 'e' in vowels‬‬
‫‪True‬‬

‫)'‪>>> letters = set('alice‬‬


‫)‪>>> letters.intersection(vowels‬‬
‫}'‪{'a', 'e', 'i‬‬

‫)'‪>>> vowels.add('x‬‬
‫‪>>> vowels‬‬
‫}'‪{'i', 'a', 'u', 'o', 'x', 'e‬‬

‫)‪>>> len(vowels‬‬
‫‪6‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪199 ‬‬

‫‪ - frozenset .2-4-5‬مجموعههای تغییر ناپذیر‬


‫‪frozenset‬ها در واقع نروعغ از مزموعرهها در پرایتون هسرتند پره پرس از سراخت‪،‬‬
‫نمغتوانمم آن را تغممر دهمم‪ .‬یک از قابلمتهای خروب ‪frozenset‬هرا ایرن اسرت پره از‬
‫آنها مغتوان به عنوان پلمد یرک دیاشرنری یرا بره عنروان عنصرری از یرک ‪ set‬دیگرر‬
‫استفاده نمود‪.‬‬

‫)}'‪>>> vowels = frozenset({'a', 'e', 'i', 'o', 'u‬‬


‫)'‪>>> vowels.add('p‬‬
‫‪AttributeError:‬‬
‫"'‪"'frozenset' object has no attribute 'add‬‬

‫‪# Frozensets are hashable and can‬‬


‫‪# be used as dictionary keys:‬‬
‫} '‪>>> d = { frozenset({1, 2, 3}): 'hello‬‬
‫])}‪>>> d[frozenset({1, 2, 3‬‬
‫'‪'hello‬‬

‫‪1‬‬
‫‪ - collections.Counter .3-4-5‬مجموعههای چندتایی‬
‫این پالس در پایتون یک مزموعهی چنردتایغ را پمراده سرازی مغپنرد پره عناصرر‬
‫موجود در یک مزموعه مغتوانند بمی از یک عنصر داشته باشند‪.‬‬
‫با استفاده از این نو مزموعهها مغتوان وجود یا عدم وجود یک عنصر در مزموعه‬
‫را بررسغ پرده و همچنمن مغتوان تعداد دفعرات رخرداد آن عنصرر را در مزموعره نمرز‬
‫بدست آورد‪.‬‬

‫‪1 multisets‬‬
‫‪  200‬ترفندهای پایتون‬

‫‪>>> from collections import Counter‬‬


‫)(‪>>> inventory = Counter‬‬

‫}‪>>> loot = {'sword': 1, 'bread': 3‬‬


‫)‪>>> inventory.update(loot‬‬
‫‪>>> inventory‬‬
‫)}‪Counter({'bread': 3, 'sword': 1‬‬

‫}‪>>> more_loot = {'sword': 1, 'apple': 1‬‬


‫)‪>>> inventory.update(more_loot‬‬
‫‪>>> inventory‬‬
‫)}‪Counter({'bread': 3, 'sword': 2, 'apple': 1‬‬

‫توجه داشته باشمد په تابع ‪ len‬روی این پالس‪ ،‬تعداد عناصر منحصرر بره فررد را بره‬
‫شما نمایی مغدهد‪ .‬برای بدست آوردن مزمو تمام عناصر موجرود در ایرن ‪multiset‬‬
‫مغتوانمد از تابع ‪ sum‬استفاده نمایمد‪.‬‬

‫)‪>>> len(inventory‬‬
‫‪3 # Unique elements‬‬

‫))(‪>>> sum(inventory.values‬‬
‫‪6 # Total no. of elements‬‬

‫‪ .4-4-5‬نکات کلیدی در هنگام کار با مجموعهها در پایتون‬


‫شما مغتوانمد از انو این مزموعهها در برنامههای خود بسته به نماز استفاده پنمد‪:‬‬
‫‪ ‬در صورتغ په قصد دارید تا مزموعرهای برا قابلمرت تغممرر عناصرر ایزراد پنمرد‪،‬‬
‫پمشنهاد مغشود از نو ساختار دادهی پمی فرض ‪ set‬در پایتون استفاده پنمد‪.‬‬
‫‪ ‬اشماء ‪ frozenset‬را مغتوانمد به عنوان پلمد یک دیاشنری یا عنصرری از یرک‬
‫‪ set‬بسته به نماز خود استفاده پنمد‪.‬‬
‫‪ ‬از ‪ collections.Counter‬مغتوانمد به عنوان یک ساختار داده با قابلمت بدسرت‬
‫آوردن دفعات تارار هر عنصر در یک مزموعه استفاده پنمد‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪201 ‬‬

‫‪ .5-5‬پشتهها‪ 1‬در پایتون (‪)LIFOs‬‬


‫پشتهها شامل مزموعهای از اشرماء هسرتند پره بره صرورت ‪ first-out ،last-in‬عمرل‬
‫مغپنند‪ .‬بدین معنا په آخرین عنصری په به آن افزوده مغشود‪ ،‬به عنوان اولرمن عنصرر‬
‫در هنگام حذف از پشته خارج مغشود‪ .‬برخالف آرایه و لمست‪ ،‬در پشتهها نمغتروان بره‬
‫تمام عناصر آن به صورت دلخواه دسترسغ داشت‪ .‬بره عمرل افرزودن و حرذف در پشرته‬
‫‪ push‬و ‪ pop‬مغگویند‪.‬‬
‫برای درک بهتر این نو ساختار داده بماید به یرک مثرال در دنمرای واقعرغ بپرردازیم‪.‬‬
‫فرض پنمد تعداد زیادی بشقاب روی هرم قررار دارنرد‪ .‬در ایرن حالرت تنهرا مغتروان بره‬
‫باالترین بشقاب دسترسغ داشت و آن را از روی پشتهای از بشقابها برداشت یا بشرقاب‬
‫دیگری بر روی آن افزود‪ .‬همچنمن برای دسترسغ به یاغ از بشقابهای پایمنغ باید تمام‬
‫بشقابهای روی آن را برداشت‪.‬‬
‫پشتهها و صفها‪ 2‬بسمار شبمه به یادیگر هستند‪ .‬هر دو مزموعه به عنوان عناصری از‬
‫یک ساختار دادهی خطغ در نظر گرفته مغشوند‪ .‬در صف برخالف پشته‪ ،‬مزموعهای از‬
‫عناصر به صورت ‪ first-out ،first-in‬عمل مغپننرد‪ .‬یعنرغ اولرمن عنصرری پره افرزوده‬
‫مغشود‪ ،‬در هنگام حذف نمز به عنوان اولمن عنصر از مزموعه داده خارج مغشود‪.‬‬
‫از لحاظ عملارد‪ ،‬هزینه افزودن و حذف یک عنصر در پشته هزینهای برابرر برا )‪O(1‬‬
‫دارد‪.‬‬
‫پشتهها در بسماری از الگوریتمهای مختلف در دنمای پامپموتر نمز اسرتفاده مغشروند‪.‬‬
‫یاغ از نمونههای پاربرد پشتهها در الگوریتم ‪ 3DFS‬است‪.‬‬
‫در پایتون انوا مختلفغ از پشتهها پماده سازی شده است په بسرته بره نمراز خرود مرغ‬
‫توانمد از ویژگغهرای هرپردام از آنهرا در برنامرهی خودتران اسرتفاده نمایمرد‪ .‬در ادامره‬
‫نگاهغ به برخغ از انوا پشتهها در پایتون مغاندازیم‪.‬‬

‫‪1 stack‬‬
‫‪2 queue‬‬
‫‪3 depth-first search‬‬
‫‪  202‬ترفندهای پایتون‬

‫‪ - list .1-5-5‬یک پشتهی ساده‬


‫از آنزا په لمستها در پایتون از عملمرات ‪ push‬و ‪ pop‬پشرتمبانغ مغپننرد‪ ،‬از آنهرا‬
‫مغتوان به عنوان ساختار دادهی پشته در پایتون نمز استفاده نمود‪.‬‬
‫در واقع لمستها در پایتون به عنوان نوعغ از ساختار دادهی آرایه شرناخته مغشروند‪.‬‬
‫این بدان معناست په فضای حافظهی اشغال شده توسط یک لمست در هنگام حرذف یرا‬
‫افزودن عناصر به آن تغممر مغپند‪.‬‬
‫به منظور ایناه در هنگام حذف یا افزودن یک عنصر بره لمسرت از هزینرهای برابرر برا‬
‫)‪ O(1‬بهره ببریم‪ ،‬باید از متد ‪ append‬برای افزودن یک عنصر به انتهای لمست و از مترد‬
‫‪ pop‬برای حذف یک عنصر از انتهای لمست استفاده پنمم‪.‬‬

‫>>>‬ ‫][ = ‪s‬‬


‫>>>‬ ‫)'‪s.append('eat‬‬
‫>>>‬ ‫)'‪s.append('sleep‬‬
‫>>>‬ ‫)'‪s.append('code‬‬

‫‪>>> s‬‬
‫]'‪['eat', 'sleep', 'code‬‬

‫)(‪>>> s.pop‬‬
‫'‪'code‬‬
‫)(‪>>> s.pop‬‬
‫'‪'sleep‬‬
‫)(‪>>> s.pop‬‬
‫'‪'eat‬‬

‫)(‪>>> s.pop‬‬
‫"‪IndexError: "pop from empty list‬‬

‫‪ - collections.deque .2-5-5‬پشتههایی سریع و قدرتمند‬


‫پالس ‪ deque‬در پایتون به صورت یک پشته برا دو انتهرا (‪ )double-ended‬عمرل‬
‫مغپند په مغتوان عملمات حذف و افزودن یک عنصر بره ایرن نرو پشرته را از هرر دو‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪203 ‬‬

‫انتهای آن با هزینهی )‪ O(1‬انزام داد‪ .‬این قابلمت باعث مغشود په بتروان از ایرن پرالس‬
‫هم به عنوان پشته و هم به عنوان صف در پایتون استفاده پرد‪.‬‬
‫نقطهی ضعف این پالس در دسترسغ بره عنصرری در وسرط آن اسرت پره هزینرهی‬
‫برابر با )‪ O(n‬برای ما دارد‪.‬‬
‫به طور پلغ ‪ deque‬مغتواند بهترین انتخاب شما برای پار با پشتهها در برنامرههایتان‬
‫باشد‪.‬‬

‫>>>‬ ‫‪from collections import deque‬‬


‫>>>‬ ‫)(‪s = deque‬‬
‫>>>‬ ‫)'‪s.append('eat‬‬
‫>>>‬ ‫)'‪s.append('sleep‬‬
‫>>>‬ ‫)'‪s.append('code‬‬

‫‪>>> s‬‬
‫)]'‪deque(['eat', 'sleep', 'code‬‬

‫)(‪>>> s.pop‬‬
‫'‪'code‬‬
‫)(‪>>> s.pop‬‬
‫'‪'sleep‬‬
‫)(‪>>> s.pop‬‬
‫'‪'eat‬‬
‫)(‪>>> s.pop‬‬
‫"‪IndexError: "pop from an empty deque‬‬

‫‪ - queue.LifoQueue .3-5-5‬پشتهای مناسب برای محاسبات موازی‬


‫این نو از پشته در پایتون برای پردازشهایغ په به صورت همزمران اجررا مغشروند‬
‫پماده سازی شده است و از قابلمت قفل پرردن برنامرهی در حرال اجررا اسرتفاده مغپنرد‪.‬‬
‫بدین معنا په این قابلمت را دارد تا از تعداد زیادی پردازش به منظور پرار برا یرک پشرته‬
‫برای افزودن یک عنصر یا حذف یک عنصر از پشتهی مورد نظر استفاده پند‪.‬‬
‫بسته به نماز شما در برنامههایتان‪ ،‬مغتوانمد از این پشته استفاده پنمد‪.‬‬
‫‪  204‬ترفندهای پایتون‬

‫>>>‬ ‫‪from queue import LifoQueue‬‬


‫>>>‬ ‫)(‪s = LifoQueue‬‬
‫>>>‬ ‫)'‪s.put('eat‬‬
‫>>>‬ ‫)'‪s.put('sleep‬‬
‫>>>‬ ‫)'‪s.put('code‬‬

‫‪>>> s‬‬
‫>‪<queue.LifoQueue object at 0x108298dd8‬‬

‫)(‪>>> s.get‬‬
‫'‪'code‬‬
‫)(‪>>> s.get‬‬
‫'‪'sleep‬‬
‫)(‪>>> s.get‬‬
‫'‪'eat‬‬

‫)(‪>>> s.get_nowait‬‬
‫‪queue.Empty‬‬

‫)(‪>>> s.get‬‬
‫‪# Blocks / waits forever...‬‬

‫‪ .4-5-5‬نکات کلیدی در هنگام کار با پشتهها در پایتون‬


‫همانطور په مشاهده نمودید انوا مختلفغ از پشتهها در پایتون پماده سازی شده است‬
‫په مغتوان از آنها بسته به نماز خود استفاده نمود‪.‬‬
‫در صورتغ په قصد ندارید در برنامهی خود از پردازشهایغ بره صرورت همزمران و‬
‫مرروازی اسررتفاده پنمررد‪ ،‬بایررد بررمن ‪ list‬و ‪ collections.deque‬در پررایتون یررک پرردام را‬
‫انتخاب پنمد‪.‬‬
‫لمست ها در واقع نوعغ از آرایهها تلقغ مغشوند په به شما این قابلمت را مغدهرد ترا‬
‫به عناصر مختلف در لمست بسمار سریع دسترسغ داشته باشمد؛ امرا در هنگرام افرزودن یرا‬
‫حذف عنصری از آن نماز به تغممر فضای حافظهی آن است‪ .‬البته لمسرتها مقردار فضرای‬
‫حافظهی بمشتری نسبت به عناصرشان اشغال مغپنند ترا بررای عملمرات ‪ push‬و ‪ pop‬در‬
‫پشته نماز به تغممر سایز در هر بار استفاده از این متدها نباشد‪ .‬اما باید توجه داشته باشمد په‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪205 ‬‬

‫برای ایناار باید از متدهای ‪ append‬و ‪ pop‬استفاده پنمد ترا هزینرهای مناسرب برابرر برا‬
‫)‪ O(1‬برای عملمات خود داشته باشمد‪.‬‬
‫پالس ‪ collections.deque‬در پایتون به صورت یک پشته با دو جهت پماده سرازی‬
‫شده است په عملمات حذف یا افزودن عنصری به آن را مغتوان از هر سمت انزام داده‬
‫و هزینهای برابر با )‪ O(1‬خواهد داشت‪.‬‬
‫به طور پلغ بهتر است برای پماده سرازی پشرتهها در پرایتون از ‪collections.deque‬‬
‫استفاده پنمم‪.‬‬

‫‪ .6-5‬صفها‪ 1‬در پایتون (‪)FIFOs‬‬


‫در این بخی با نحوهی پار با صفها در پایتون آشنا مغشرویم‪ .‬امرا قبرل از آن بهترر‬
‫است نگاهغ به تعریف صف بمندازیم‪:‬‬
‫یک صف شامل مزموعهای از اشماء است په به صرورت ‪ first-out ،first-in‬عمرل‬
‫مغپن د‪ .‬یعنغ اولمن شغء وارد شده به یک صف به عنوان اولمن شغء در هنگام عملمات‬
‫حذف از آن خارج مغشود‪ .‬برخالف لمستها یا آرایهها‪ ،‬در صفها اماان دسترسغ بره‬
‫عناصری په در وسط صف هستند وجود ندارد‪.‬‬
‫فرض پنمد صفغ از برنامهنویسان برای ورود به پنفرانس ‪ PyCon‬در حرال برت نرام‬
‫هستند‪ .‬هر فرد جدیدی به انتهای صف اضافه مغشود و هر فردی په بت نام آن به اتمام‬
‫برسد از جلوی صف خارج مغشود‪.‬‬
‫صفها همانند پشتهها هستند و تنها تفاوت آنهرا در نحروهی حرذف یرک عنصرر از‬
‫آنها است‪ .‬در صف اولمن عنصری په به صف اضافه شده است در هنگام حذف خارج‬
‫مغشود‪ .‬اما در پشته آخرین عنصری په به آن اضافه شده است در هنگرام حرذف از آن‬
‫خارج مغشود‪.‬‬

‫‪1 Queue‬‬
‫‪  206‬ترفندهای پایتون‬

‫از لحاظ عملارد‪ ،‬عملمات حذف و افزودن عنصری به یک صرف هزینرهای برابرر برا‬
‫)‪ O(1‬دارد و بنابراین این عملمات سریع انزام مغشود‪.‬‬
‫از صفها در بسماری از برنامهها و الگوریتمها در علروم پرامپموتر اسرتفاده مغشرود‪.‬‬
‫یاغ از الگوریتمهای رایزغ په از ایرن سراختار داده اسرتفاده مغپنرد‪ ،‬الگروریتم ‪BFS1‬‬
‫است‪.‬‬
‫الگوریتمهررای زمرران بنرردی در پررامپموتر معمرروال از صررفهای بررا اولویررت اسررتفاده‬
‫مغپنند‪ .‬برخالف صفهای عادی په عناصر را بر اساس ترتمرب ورود از صرف خرارج‬
‫مغپند‪ ،‬صفهای با اولویت در هنگام خارج پرردن یرک عنصرر از صرف بره اولویرت‬
‫بره هرر‬ ‫عناصر توجه مغپند‪ .‬اولویت عناصر در یک صف بر اساس پلمردی مخصرو‬
‫عنصر مشرخص مغشرود‪ .‬در بخری آینرده بره توضرمحات بمشرتری در مرورد صرفهای‬
‫اولویت دار مغپردازیم‪.‬‬
‫صفهای عادی در پایتون به صورتهای مختلفغ پماده سرازی شردهاند‪ .‬در ادامره بره‬
‫بررسغ هرپدام خواهمم پرداخت‪.‬‬

‫‪ - list .1-6-5‬صفهایی بسیار کند‬


‫مغتوانمم از لمستها در پایتون به عنوان صف در برنامههای خود اسرتفاده پنرمم‪ .‬امرا‬
‫این عمل در لمستها به دلمل ایناه افزودن و حذف یک عنصر از ابتردای صرف نمراز بره‬
‫جابزایغ تمام عناصر موجود در لمست دارد‪ ،‬بسمار پنرد بروده و هزینرهای برابرر برا )‪O(n‬‬
‫دارد‪.‬‬
‫بنابراین‪ ،‬به همک عنوان پمشنهاد نمغشود په از لمستها به عنوان ساختار دادهی صرف‬
‫در برنامههای خود استفاده پنمد‪.‬‬

‫‪1 breadth-first search‬‬


‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪207 ‬‬

‫>>>‬ ‫][ = ‪q‬‬


‫>>>‬ ‫)'‪q.append('eat‬‬
‫>>>‬ ‫)'‪q.append('sleep‬‬
‫>>>‬ ‫)'‪q.append('code‬‬

‫‪>>> q‬‬
‫]'‪['eat', 'sleep', 'code‬‬

‫!‪# Careful: This is slow‬‬


‫)‪>>> q.pop(0‬‬
‫'‪'eat‬‬

‫‪ - collections.deque .2-6-5‬صفهایی سریع و قدرتمند‬


‫پالس ‪ deque‬صفهایغ با دو انتها (‪ )double-ended‬در پایتون هستند پره بره مرا‬
‫قابلمت افزودن و حذف عناصر را از هر دو سمت آن داده و هزینهای برابر برا )‪ O(1‬دارد‪.‬‬
‫از آن جهت په ‪ deque‬از هر دو انتها مغتواند عناصری را حذف یا اضافه نماید‪ ،‬از این‬
‫پالس در پایتون مغتوان هم به عنوان صف و هم به عنوان پشته استفاده نمود‪.‬‬
‫نقطهی ضعف این پالس در دسترسغ بره عنصرری در وسرط آن اسرت پره هزینرهی‬
‫برابر با )‪ O(n‬برای ما دارد‪.‬‬
‫به طور پلغ ‪ deque‬مغتواند بهترین انتخاب شما برای پار با صفها در برنامههایتان‬
‫باشد‪.‬‬
‫‪  208‬ترفندهای پایتون‬

‫>>>‬ ‫‪from collections import deque‬‬


‫>>>‬ ‫)(‪q = deque‬‬
‫>>>‬ ‫)'‪q.append('eat‬‬
‫>>>‬ ‫)'‪q.append('sleep‬‬
‫>>>‬ ‫)'‪q.append('code‬‬

‫‪>>> q‬‬
‫)]'‪deque(['eat', 'sleep', 'code‬‬

‫)(‪>>> q.popleft‬‬
‫'‪'eat‬‬
‫)(‪>>> q.popleft‬‬
‫'‪'sleep‬‬
‫)(‪>>> q.popleft‬‬
‫'‪'code‬‬
‫)(‪>>> q.popleft‬‬
‫"‪IndexError: "pop from an empty deque‬‬

‫‪ - queue.Queue .3-6-5‬صفهایی مناسب برای محاسبات موازی‬


‫این نو از صفها در پایتون برای پردازشهایغ په به صورت همزمان اجرا مغشوند‬
‫پماده سازی شده است و از قابلمت قفل پرردن برنامرهی در حرال اجررا اسرتفاده مغپنرد‪.‬‬
‫بدین معنا په این قابلمت را دارد تا از تعداد زیادی پردازش به منظور پار برا یرک صرف‬
‫برای افزودن یک عنصر یا حذف یک عنصر از صف مورد نظر‪ ،‬استفاده پند‪.‬‬
‫این نو از صف هرا قابلمرت ‪ multi producer/ multi consumer‬را پمراده سرازی‬
‫مغپنند په برای پردازشهای موازی بسمار مفمد است‪ .‬بسته به نماز شما در برنامرههایتان‪،‬‬
‫مغتوانمد از این صف استفاده پنمد‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪209 ‬‬

‫>>>‬ ‫‪from queue import Queue‬‬


‫>>>‬ ‫)(‪q = Queue‬‬
‫>>>‬ ‫)'‪q.put('eat‬‬
‫>>>‬ ‫)'‪q.put('sleep‬‬
‫>>>‬ ‫)'‪q.put('code‬‬

‫‪>>> q‬‬
‫>‪<queue.Queue object at 0x1070f5b38‬‬

‫)(‪>>> q.get‬‬
‫'‪'eat‬‬
‫)(‪>>> q.get‬‬
‫'‪'sleep‬‬
‫)(‪>>> q.get‬‬
‫'‪'code‬‬

‫)(‪>>> q.get_nowait‬‬
‫‪queue.Empty‬‬

‫)(‪>>> q.get‬‬
‫‪# Blocks / waits forever...‬‬

‫‪ - multiprocessing.Queue .4-6-5‬صفهای مشترک‬


‫این نو از صفها به ما این قابلمت را مغدهد تا از آنها در یرک برنامره بره صرورت‬
‫موازی در ‪worker‬های مختلف اسرتفاده پنرمم‪ .‬از ایرن صرفها بره منظرور بره اشرتراک‬
‫گذاری دادهها بمن فرایندهای مختلف در یرک برنامره نمرز اسرتفاده مغشرود‪ .‬ایرن صرف‬
‫مغتواند هر نو شغء در پایتون را ذخمره پرده و به فرایندهای دیگر منتقل نماید‪.‬‬
‫به دلمل ‪ ،GIL1‬پردازشهای مروازی در ‪ CPython‬محبروب اسرت‪ .‬برا اسرتفاده از ایرن‬
‫صف مغتوان پردازشغ را بمن چندین پردازشگر در سمستم توزیع نموده تا بتوان تا حدی‬
‫از محدودیتهای ‪ GIL‬فارغ شد‪.‬‬

‫‪1 Global Intrepreter Lock‬‬


‫‪  210‬ترفندهای پایتون‬

‫>>>‬ ‫‪from multiprocessing import Queue‬‬


‫>>>‬ ‫)(‪q = Queue‬‬
‫>>>‬ ‫)'‪q.put('eat‬‬
‫>>>‬ ‫)'‪q.put('sleep‬‬
‫>>>‬ ‫)'‪q.put('code‬‬

‫‪>>> q‬‬
‫>‪<multiprocessing.queues.Queue object at 0x1081c12b0‬‬

‫)(‪>>> q.get‬‬
‫'‪'eat‬‬
‫)(‪>>> q.get‬‬
‫'‪'sleep‬‬
‫)(‪>>> q.get‬‬
‫'‪'code‬‬

‫)(‪>>> q.get‬‬
‫‪# Blocks / waits forever...‬‬

‫‪ .5-6-5‬نکات کلیدی در هنگام کار با صفها در پایتون‬


‫در پایتون انوا مختلفغ از صفها با ویژگغهای متفاوت پماده سازی شده اسرت‪ .‬بره‬
‫طور پلغ پمشنهاد نمغشود از لمست به دلمل عملارد پند آن به عنوان نو دادهی صرف‬
‫استفاده شود‪ .‬در صورتغ په به دنبال صفغ نمستمد په در پردازشهای موازی خود از آن‬
‫استفاده پنمد‪ ،‬بهتر است تا از ‪ collections.deque‬به عنروان نرو دادهی ‪ FIFO‬اسرتفاده‬
‫نمایمد‪.‬‬

‫‪ .7-5‬صفهای با اولویت‬
‫صفهای دارای اولویت‪ ،‬نوعغ از صفها هستند په در آنها هر عنصر دارای یرک‬
‫پلمد بوده په این پلمد مشخص پنندهی اولویت عنصر بروده و معمروال بره عنروان یرک‬
‫مغیابد‪.‬‬ ‫ارزش وزنغ به عناصر اختصا‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪211 ‬‬

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

‫‪ - list .1-7-5‬نگهداری یک صف به صورت دستی‬


‫از لمستها مغتوان به عنوان یک صف به منظرور پرار برا صرفهای دارای اولویرت‬
‫استفاده نمود‪ .‬نقطه ضعف استفاده از این روش در هزینهی افزودن یک عنصر به آن بوده‬
‫په برابر با )‪ O(n‬است‪.‬‬
‫همچنمن عملمات مرتب سازی در این لمست نمز په باید به صورت دستغ انزام شرود‬
‫برابر با )‪ O(n‬است‪ .‬شما به عنوان یک برنامه نویس حتمرا بایرد پرس از افرزودن عنصرری‬
‫جدید به این صف‪ ،‬عمل مرتب سازی را انزام دهمد تا در روند اجرای برنامهی خود بره‬
‫مشال نخورید‪.‬‬
‫بنابراین بهتر است تنها در صورتغ از ‪ list‬به عنروان یرک صرف اولویرت دار اسرتفاده‬
‫پنمد په تعداد دادههای مورد نماز برای ذخمره سازی درون آن پم است‪.‬‬
‫‪  212‬ترفندهای پایتون‬

‫][ = ‪q‬‬
‫))'‪q.append((2, 'code‬‬
‫))'‪q.append((1, 'eat‬‬
‫))'‪q.append((3, 'sleep‬‬

‫‪# NOTE: Remember to re-sort every time‬‬


‫‪# a new element is inserted, or use‬‬
‫‪# bisect.insort().‬‬
‫)‪q.sort(reverse=True‬‬

‫‪while q:‬‬
‫)(‪next_item = q.pop‬‬
‫)‪print(next_item‬‬

‫‪#‬‬ ‫‪Result:‬‬
‫‪#‬‬ ‫)'‪(1, 'eat‬‬
‫‪#‬‬ ‫)'‪(2, 'code‬‬
‫‪#‬‬ ‫)'‪(3, 'sleep‬‬

‫‪ - heapq .2-7-5‬پشتههای دودویی‬


‫این نو از ساختار داده په پایهی آن همان ‪ list‬در پایتون است‪ ،‬عملمرات افرزودن یرا‬
‫حذف پردن عناصر از درون آن با هزینهی )‪ O(log n‬انزام مغشود‪.‬‬
‫استفاده از این ماژول یاغ از بهترین روش ها به منظور پار با صرفهای برا اولویرت‬
‫است‪ heapq .‬عناصر موجرود در خرود را بطرور خودپرار بره صرورت صرعودی مرترب‬
‫مغپند‪.‬‬
‫فصل پنجم‪ :‬ساختارهای داده در پایتون ‪213 ‬‬

‫‪import heapq‬‬

‫][ = ‪q‬‬

‫))'‪heapq.heappush(q, (2, 'code‬‬


‫))'‪heapq.heappush(q, (1, 'eat‬‬
‫))'‪heapq.heappush(q, (3, 'sleep‬‬

‫‪while q:‬‬
‫)‪next_item = heapq.heappop(q‬‬
‫)‪print(next_item‬‬

‫‪#‬‬ ‫‪Result:‬‬
‫‪#‬‬ ‫)'‪(1, 'eat‬‬
‫‪#‬‬ ‫)'‪(2, 'code‬‬
‫‪#‬‬ ‫)'‪(3, 'sleep‬‬

‫‪ - queue.PriorityQueue .3-7-5‬مناسب ترین نوع از صفهای با اولویت‬


‫این نو از صفها در واقع از همان سراختار درونرغ ‪ heapq‬اسرتفاده مغپننرد‪ .‬تنهرا‬
‫تفاوت آن با ‪ heapq‬در قابلمت پشتمبانغ آن از وظایف همزمران بره منظرور پرار برا چنرد‬
‫روند در برنامه است‪.‬‬
‫شررما بررر اسرراس نمرراز خررود مغتوانمررد از ‪( heapq‬برره صررورت ‪ )function-based‬یررا‬
‫‪( queue.PriorityQueue‬به صورت ‪ )class-based‬در برنامههای خود استفاده پنمد‪.‬‬
‫‪  214‬ترفندهای پایتون‬

‫‪from queue import PriorityQueue‬‬

‫)(‪q = PriorityQueue‬‬

‫))'‪q.put((2, 'code‬‬
‫))'‪q.put((1, 'eat‬‬
‫))'‪q.put((3, 'sleep‬‬

‫‪while not q.empty():‬‬


‫)(‪next_item = q.get‬‬
‫)‪print(next_item‬‬

‫‪#‬‬ ‫‪Result:‬‬
‫‪#‬‬ ‫)'‪(1, 'eat‬‬
‫‪#‬‬ ‫)'‪(2, 'code‬‬
‫‪#‬‬ ‫)'‪(3, 'sleep‬‬

‫‪ .4-7-5‬نکات کلیدی در هنگام کار با صفهای با اولویت در پایتون‬


‫در پررایتون انرروا مختلفررغ از صررفهای بررا اولویررت وجررود دارد پرره مغترروانمم در‬
‫برنامههای خود از آنها استفاده پنمم‪.‬‬
‫پمشنهاد مغشود در اپثر مواقرع از ‪ queue.PriorityQueue‬در برنامرههایتان اسرتفاده‬
‫پرده و در صورتغ په قصد ندارید برنامههایغ با پردازش موازی و چند نخغ بنویسمد از‬
‫‪ heapq‬استفاده نمایمد‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون‬

‫فصل ششم‬

‫حلقهها در پایتون‬
‫‪ .1-6‬نوشتن حلقههای پایتونی‬
‫دانستن چگونگغ نوشتن حلقهها یاغ از اساسغ ترین موارد برای برنامه نویسغ در هر‬
‫زبانغ است‪ .‬اما نحوهی نوشتن حلقهها در پایتون پمغ برا سرایر زبانهرا ماننرد ‪ C‬یرا ‪Java‬‬
‫متفاوت است‪.‬‬
‫برای مثال در قطعه پد زیر‪ ،‬برنامه نویس سعغ پرده است یک حلقه را هماننرد زبران‬
‫‪ C‬یا ‪ Java‬بنویسد‪.‬‬

‫]'‪my_items = ['a', 'b', 'c‬‬

‫‪i = 0‬‬

‫‪while i < len(my_items):‬‬


‫)]‪print(my_items[i‬‬
‫‪i += 1‬‬

‫حال سؤال پمی مغآید په چرا پد باال به صورت پایتونغ نوشته نشده است؟‬
‫ناتهی اول ایناه برنامه نویس اندیس ‪ i‬را به صورت دستغ ایزراد پررده و هربرار در‬
‫انتهای حلقه آن را افزایی مغدهد؛ و ناته دوم‪ :‬برنامه نویس برای بدسرت آوردن طرول‬
‫لمست از تابع ‪ len‬استفاده پرده است‪.‬‬
‫ما در پایتون مغتوانمم حلقهها را به صورتغ بسمار ساده تر پمراده سرازی پنرمم پره دو‬
‫عمل باال را به صورت خودپار برای ما انزام مغدهد‪ .‬بدین ترتمرب الزم نمسرت انردیس‬
‫یک لمست را به صورت دسرتغ ایزراد پررده و آن را مردیریت پنمرد‪ .‬بنرابراین حلقرهی‬
‫پایتونغ ما بسمار ساده تر و خواناتر مغشود‪.‬‬
‫حال حلقهی باال را بهبود مغبخشمم و آن را به صورت پایتونغ پماده سرازی خرواهمم‬
‫نمود‪ .‬برای این پار از تابع درونغ ‪ range‬استفاده مغپنمم په بصورت خودپار انردیس‬
‫را برای ما مدیریت مغپند‪.‬‬
‫‪  218‬ترفندهای پایتون‬

‫))‪>>> range(len(my_items‬‬
‫)‪range(0, 3‬‬

‫))‪>>> list(range(0, 3‬‬


‫]‪[0, 1, 2‬‬

‫نتمزهی حاصل شده از ‪ range‬یک نو دادهی تغممر ناپذیر اسرت‪ .‬مزیرت اسرتفاده از‬
‫‪ range‬بزای ‪ list‬در اشغال فضای حافظهی پمتر است‪ range .‬مقادیر یک لمست را بره‬
‫صورت جداگانه ذخمره نمغپند و تنها به صورت یک شغء تارار شونده پار مغپند‪.‬‬

‫‪for i in range(len(my_items)):‬‬
‫)]‪print(my_items[i‬‬

‫حتغ پد باال را باز هم مغتوان بهبرود بخشرمد‪ .‬چررا پره اسرتفاده از ‪ range‬و ‪ len‬بره‬
‫منظور ایزاد یک حلقه در پایتون مناسب نمست‪ .‬بنابراین بمایمرد برازهم پرد براال را تغممرر‬
‫دهمم‪.‬‬
‫همانطور په قبال هم به آن اشاره پردیم‪ ،‬هرر حلقره در پرایتون بره صرورت خودپرار‬
‫اندیس یک لمست را مدیریت مغپند و به صورت یک حلقهی ‪ for-each‬عمل مغپند‪.‬‬
‫شاید بهتر باشد پد باال را به صورت زیر بنویسمم‪.‬‬

‫‪for item in my_items:‬‬


‫)‪print(item‬‬

‫قطعه پد باال‪ ،‬مناسب ترین روش برای ایزاد یک حلقه در پایتون است‪ .‬همانطور په‬
‫مشاهده مغپنمد در این حلقه بدون توجره بره انردیس یرک لمسرت‪ ،‬مغتروان روی تمرام‬
‫مقادیر موجود در لمست پممایی انزام داد‪ .‬همچنمن توجه داشته باشمد په مقادیر موجود‬
‫در لمست به همان ترتمبغ په قرار دارند در حلقه مورد پممایی قرار مغگمرند‪.‬‬
‫شاید در یک برنامه ما نماز به اندیس هر عنصر موجود در لمست نمرز داشرته باشرمم‪ .‬برا‬
‫قطعه پد باال نمغتوانمم به اندیس موجود دسترسغ داشته باشمم‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪219 ‬‬

‫در پایتون بدون استفاده از ‪ range‬و ‪ len‬هم مغتوان حلقههایغ ایزاد پرد په اندیس‬
‫عناصر را نمز مدیریت مغپند‪ .‬برا اسرتفاده از ترابع درونرغ ‪ enumerate‬مغتروانمم روی‬
‫مقادیر و اندیس آنها در یک لمست پممایی انزام دهمم‪.‬‬

‫‪>>> for i, item in enumerate(my_items):‬‬


‫)'}‪print(f'{i}: {item‬‬

‫‪0: a‬‬
‫‪1: b‬‬
‫‪2: c‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬در قطعه پد باال مغتوانمم در یک حلقره بره عناصرر و‬
‫اندیس آنها دسترسغ داشته باشمم‪ .‬همچنمن در پایتون مغتروانمم روی پلمردها و مقرادیر‬
‫موجود در دیاشنری نمز پممایی انزام داده و برای آن یک حلقه ایزاد نمایمم‪.‬‬

‫{ = ‪>>> emails‬‬
‫‪'Bob': '[email protected]',‬‬
‫‪'Alice': '[email protected]',‬‬
‫}‬

‫‪>>> for name, email in emails.items():‬‬


‫)'}‪print(f'{name} -> {email‬‬

‫'‪'Bob -> [email protected]‬‬


‫'‪'Alice -> [email protected]‬‬

‫در انتهای این بخی الزم است تا در مورد یک موضرو مهرم دیگرر در حلقرهها نمرز‬
‫صحبت پنمم‪ .‬شاید شما قصد داشته باشمد تا یک حلقه ایزاد پرده و اندازهی گام حلقه‬
‫(‪ )step size‬را نمز بخواهمد در آن مدیریت پنمد‪ .‬برای نمونه حلقهی جاوایغ زیرر را در‬
‫نظر بگمرید‪.‬‬

‫{ )‪for (int i = a; i < n; i += s‬‬


‫‪// ...‬‬
‫}‬
‫‪  220‬ترفندهای پایتون‬

‫قطعه پد باال به چه صورت باید در پایتون نوشته شود؟ در این مواقرع بایرد دوبراره از‬
‫تابع ‪ range‬استفاده پنمم‪ .‬این تابع مغتوانرد مقرادیری بره عنروان نقطرهی شررو (‪)start‬‬
‫برای حلقه‪ ،‬نقطهی پایان (‪ )stop‬و اندازهی گام (‪ )step‬را از برنامه نویس دریافت پنرد‪.‬‬
‫بنابراین قطعه پد جاوایغ باال به صورت زیر به یک قطعه پد پایتون تبدیل مغشود‪.‬‬

‫‪for i in range(start, stop, step):‬‬


‫‪# ...‬‬

‫‪ .1-1-6‬نکات کلیدی در هنگام کار با حلقهها در پایتون‬


‫به صورت پلغ بهتر است در پایتون حلقهها را به صورت قطعه پد جراوا یرا ‪ C‬پمراده‬
‫سازی نانمم و آنها را به صورت پایتونغ بنویسمم‪ .‬حلقهها در پایتون در واقع به صرورت‬
‫حلقرههای ‪ for-each‬عمررل پرررده پرره پرردازش آن روی عناصررر یررک پالاشررن انزررام‬
‫مغشود‪.‬‬

‫‪ .2-6‬ایجاد لیست در یک خط‬


‫یاغ از ویژگغهای خوب زبان پایتون‪ ،‬توانایغ ایزاد یک لمست به صورت خودپار‬
‫با حلقهی ‪ for‬در یک خط است‪ .‬شاید در ابتدا درک آن پمغ دشوار به نظرر بمایرد‪ ،‬امرا‬
‫پس از مدتغ متوجه خواهمد شد په پار با آنها بسمار ساده است‪.‬‬
‫توجه داشته باشمد په آنها دقمقا همان حلقههای ‪ for‬در پایتون بوده په با سمنتاسرغ‬
‫در یک خط نوشته مغشوند‪ .‬برای مثال به ساخت یک لمست به صورت زیر توجه‬ ‫خا‬
‫پنمد‪.‬‬

‫])‪>>> squares = [x * x for x in range(10‬‬

‫این لمست نشان دهندهی مربع اعداد ‪ 0‬تا ‪ 9‬است‪.‬‬

‫‪>>> squares‬‬
‫]‪[0, 1, 4, 9, 16, 25, 36, 49, 64, 81‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪221 ‬‬

‫در صورتغ په بخواهمد این لمست را برا اسرتفاده از حلقرهی ‪ for‬در پرایتون بنویسرمد‪،‬‬
‫قطعه پد آن به صورت زیر خواهد بود‪.‬‬

‫][ = ‪>>> squares‬‬


‫‪>>> for x in range(10):‬‬
‫)‪squares.append(x * x‬‬

‫این قطعه پد نمز بسمار ساده است‪ .‬با یک بررسغ سراده و مقایسرهی ایرن دو روش برا‬
‫یادیگر مغتوانمم به ساختار ایزاد لمست در یک خرط پرغ ببرریم‪ .‬بره صرورت پلرغ بره‬
‫منظور ایزاد یک لمست طبق یک قاعدهی مشخص‪ ،‬باید به صورت زیر عمل پنمم‪.‬‬

‫]‪values = [expression for item in collection‬‬

‫خط باال‪ ،‬مغتواند به صورت یک حلقهی ‪ for‬همانند قطعه پد زیر نمز نوشته شود‪.‬‬

‫][ = ‪values‬‬
‫‪for item in collection:‬‬
‫)‪values.append(expression‬‬

‫در قطعه پد باال‪ ،‬ابتدا یک لمست خالغ ایزاد نمرودیم ترا در ادامره مقرادیر را بره آن‬
‫اضافه نمایمم‪ .‬سپس روی تمام اعضای مزموعه (‪ )collections‬پممایی پررده و حاصرل‬
‫را به لمست اضافه مغپنمم‪ .‬این یک قاعدهی پلغ به منظور ایزاد یک لمست طبرق یرک‬
‫الگوی مشخص است په مغتوان از آن در نوشتن برنامههای خود استفاده پنمم‪.‬‬
‫حال بهتر است یک قدم جلوتر رفته و شرط ها را نمز به این حلقه اضافه پنمم‪.‬‬
‫ما مغتوانمم در هنگام ایزاد لمست خود در یک خط دستوری پایتون‪ ،‬به آن شرط نمز‬
‫اضافه نمایمم تا تصممم گمری شود پدام مقادیر به لمست افزوده شوند‪.‬‬

‫)‪>>> even_squares = [x * x for x in range(10‬‬


‫]‪if x % 2 == 0‬‬
‫‪  222‬ترفندهای پایتون‬

‫در لمست باال‪ ،‬تنها مربع اعدادی (‪ 0‬تا ‪ )9‬قرار مغگمرند پره زوج باشرند‪ .‬در اینزرا از‬
‫عملگر ‪ ٪‬به منظور محاسبهی باقممانده استفاده شده است‪ .‬لمست حاصل به صرورت زیرر‬
‫خواهد بود‪.‬‬

‫‪>>> even_squares‬‬
‫]‪[0, 4, 16, 36, 64‬‬

‫دستور باال را مغتوان به صورت یک حلقهی ‪ for‬به همراه شررط ‪ if‬نمرز هماننرد زیرر‬
‫نوشت‪.‬‬

‫][ = ‪even_squares‬‬
‫‪for x in range(10):‬‬
‫‪if x % 2 == 0:‬‬
‫)‪even_squares.append(x * x‬‬

‫همانند حالت قبل‪ ،‬برای این قطعه پد نمز مغتوان یک قاعدهی پلغ بدست آورد‪ .‬برا‬
‫این تفاوت په در این حالت یک بخی با عنوان شرط نمز باید به قاعرده اضرافه شرده ترا‬
‫نتایج را فملتر پند‪ .‬قاعدهی پلغ به صورت زیر خواهد بود‪.‬‬

‫‪values = [expression‬‬
‫‪for item in collection‬‬
‫]‪if condition‬‬

‫این دستور را مغتوان به صورت قطعه پد زیر نمز ترجمه نمود‪.‬‬

‫][ = ‪values‬‬
‫‪for item in collection:‬‬
‫‪if condition:‬‬
‫)‪values.append(expression‬‬

‫به طور پلغ بهتر است لمستهایغ په در برنامههای خود نماز داریم را در یرک خرط‬
‫ایزاد پنمم‪ .‬این پار توانایغ پد زنغ ما را در برنامه نویسغ افزایی مغدهد‪.‬‬
‫اما عالوه بر ایزاد لمست‪ ،‬در پایتون به هممن صورت مغتوانمم مزموعه و دیاشرنری‬
‫نمز ایزاد پنمم‪ .‬برای ایزاد یک مزموعه به صورت زیر عمل مغپنمم‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪223 ‬‬

‫} )‪>>> { x * x for x in range(-9, 10‬‬


‫)]‪set([64, 1, 36, 0, 49, 9, 16, 81, 25, 4‬‬

‫بر خالف لمستها در پایتون په ترتمب افزوده شدن عناصرر را نمرز نگره مرغدارد‪ ،‬در‬
‫مزموعه‪ ،‬عناصر به صورت نامرتب وجود دارند‪.‬‬
‫همچنمن برای ایزاد یک دیاشنری نمز به صورت زیر عمل مغپنمم‪.‬‬

‫} )‪>>> { x: x * x for x in range(5‬‬


‫}‪{0: 0, 1: 1, 2: 4, 3: 9, 4: 16‬‬

‫بهتر است در هنگام پد نویسغ به این صورت‪ ،‬به یک ناته توجه داشته باشمد‪ .‬هرچه‬
‫بمشتر در این روش مهارت داشته باشمد و بتوانمد لمستها یا دیاشنریهای پمچمده ایزراد‬
‫پنمد‪ ،‬ممان است خواندن پد شما برای دیگران پمرغ سرخت باشرد‪ .‬پرس بهترر اسرت‬
‫هممشه و همه جا از این روش استفاده نانمد‪.‬‬
‫به طور پلغ پمشنهاد مغشرود بمشرتر از همران روش قردیمغ (نوشرتن حلقرههای ‪)for‬‬
‫استفاده شود‪ .‬چرا په نگهداری برنامه و خوانایغ آن بمشتر است‪.‬‬

‫‪ .1-2-6‬نکات کلیدی در هنگام ساخت لیستهایی در یک خط‬


‫استفاده از این روش (ایزاد لمست در یک خط) بسمار مناسب بوده و پد نویسغ شما‬
‫را بهبود مغبخشد‪ .‬این روش در واقع راه ساده تری نسبت به نوشرتن حلقرهی ‪ for‬اسرت؛‬
‫اما در پس زممنه به همان صورت عمل مغپند‪ .‬توجه داشته باشمد په اسرتفادهی زیراد از‬
‫این روش‪ ،‬خوانایغ برنامهی شما را مشال مغپند‪.‬‬

‫‪1‬‬
‫‪ .3-6‬ترفندهایی برای برش لیستها و استفاده از عملگر سوشی‬
‫در پایتون مغتوان یک لمست را تاه تاه نمود‪ .‬به این عمل در پرایتون ‪ slicing‬گفتره‬
‫مغشود‪ .‬با استفاده از این روش مغتوان تنها به بخشغ از یک لمست په مورد نمراز اسرت‬

‫‪1 Sushi‬‬
‫‪  224‬ترفندهای پایتون‬

‫دسترسغ پمدا نمود‪ .‬برای مثال مغتوان یک لمست بزرگ را با این روش به چندین لمست‬
‫پوچاتر تقسمم پرد‪.‬‬
‫برای ایناار از عالمت براپت [] استفاده مغشود پره قاعردهی پلرغ آن بره صرورت‬
‫[‪ ]start:stop:step‬است‪.‬‬

‫]‪>>> lst = [1, 2, 3, 4, 5‬‬


‫‪>>> lst‬‬
‫]‪[1, 2, 3, 4, 5‬‬

‫]‪# lst[start:end:step‬‬
‫]‪>>> lst[1:3:1‬‬
‫]‪[2, 3‬‬

‫در مثال باال با نوشتن دستور [‪ ]1:3:1‬قصد داریم به اندیس یام تا اندیس دوم با گرام‬
‫‪ 1‬دسترسغ داشته باشمم‪ .‬توجه داشته باشمد په عردد ‪ 3‬پره در براال اسرتفاده شرده اسرت‪،‬‬
‫شامل اندیس سوم نمغشود و به یک عنصر قبل از آن اشاره مغپند‪.‬‬
‫در صورتغ په گام را در قاعدهی باال لحاظ نانمد‪ ،‬پایتون به صورت پمی فرض آن‬
‫را برابر با ‪ 1‬قرار مغدهد‪.‬‬

‫]‪>>> lst[1:3‬‬
‫]‪[2, 3‬‬

‫مغ توان تنها با استفاده از گام نمز به بخشغ از لمست دسترسرغ داشرت‪ .‬بره ایرن ترفنرد‬
‫‪ stide‬نمز گفته مغشود‪ .‬برای مثال با استفاده از دستور زیر یاغ در ممان به عناصر لمسرت‬
‫دسترسغ پمدا مغپنمم‪.‬‬

‫]‪>>> lst[::2‬‬
‫]‪[1, 3, 5‬‬

‫به عملگر ‪ :‬در پایتون پره در مثرال براال اسرتفاده شرده اسرت‪ ،‬سوشرغ (‪ )sushi‬گفتره‬
‫مغشود‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪225 ‬‬

‫یاغ دیگر از ترفندهایغ په مغتوان روی یک لمسرت اجررا پررد‪ ،‬بررعاس نمرودن‬
‫ترتمب عناصر در لمست به صورت زیر است‪.‬‬

‫]‪>>> numbers[::-1‬‬
‫]‪[5, 4, 3, 2, 1‬‬

‫در مثال باال با استفاده از دو عالمت ‪ ::‬قصد داریم به پل محتویرات لمسرت دسترسرغ‬
‫داشته باشمم؛ و در ادامه گام را برابر با ‪ 1-‬قرار دادیم تا ترتمب نمایی عناصر از آخرر بره‬
‫اول شود‪ .‬این یک ترفند جالب برای برعاس پردن ترتمب عناصر در لمسرت اسرت‪ .‬امرا‬
‫پمشررنهاد مغشررود تررا برره منظررور برررعاس پررردن ترتمررب عناصررر در یررک لمسررت‪ ،‬از‬
‫‪ list.reverse‬یا تابع ‪ reversed‬استفاده پنمم‪.‬‬
‫یاغ دیگر از ترفتندها‪ ،‬حرذف تمرام مقرادیر موجرود در لمسرت اسرت؛ بردون آناره‬
‫متغمری په به عنوان لمست تعریف شرده اسرت حرذف شرود‪ .‬ممارن اسرت در برنامرهای‬
‫بخواهمد تمام محتویات یک لمست را حذف پرده و در ادامه با محتویاتغ جدیرد برا آن‬
‫پار پنمد‪ .‬این ترفند به شما این اجازه را مغدهد تا این پار را به سادگغ انزام دهمد‪.‬‬

‫]‪>>> lst = [1, 2, 3, 4, 5‬‬


‫]‪>>> del lst[:‬‬
‫‪>>> lst‬‬
‫][‬

‫شما همچنمن مغتوانمد با استفاده از متد ‪ lst.clear‬نمز ایرن پرار را انزرام دهمرد‪ .‬البتره‬
‫توجه داشته باشمد په این متد تنها در پایتون ‪ 3‬قابل استفاده است‪.‬‬
‫عالوه بر این‪ ،‬مغتوانمم تمام عناصر یک لمست را برا ایرن روش‪ ،‬یرا مقرادیری جدیرد‬
‫جایگزین پنمم‪ .‬به مثال صفحه بعد توجه پنمد‪.‬‬
‫‪  226‬ترفندهای پایتون‬

‫‪>>> original_lst = lst‬‬


‫]‪>>> lst[:] = [7, 8, 9‬‬
‫‪>>> lst‬‬
‫]‪[7, 8, 9‬‬
‫‪>>> original_lst‬‬
‫]‪[7, 8, 9‬‬
‫‪>>> original_lst is lst‬‬
‫‪True‬‬

‫در قطعه پد باال‪ ،‬مقادیری جدید در ‪ lst‬با مقادیر قبلغ جرایگزین شرد؛ امرا متغمرر ‪lst‬‬
‫حذف نشد‪ .‬به هممن دلمل متغمرر ‪ original_lst‬نمرز پرس از آن دسرتخوش تغممرر شرده و‬
‫همان مقادیر ‪ lst‬را در خود نگه داشته است‪.‬‬
‫یاغ دیگر از پاربردهای عملگر سوشغ این است په با استفاده از آن مغتروان یرک‬
‫پپغ از تمام محتویات (نه متغمر) یک لمست ایزاد نمود‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫]‪>>> copied_lst = lst[:‬‬


‫‪>>> copied_lst‬‬
‫]‪[7, 8, 9‬‬
‫‪>>> copied_lst is lst‬‬
‫‪False‬‬

‫در قطعه پد باال‪ ،‬تنها محتویات موجود در لمست را در متغمری جدید پپرغ نمرودیم‪.‬‬
‫به هممن دلمل است په ماهمت دو متغمر ‪ lst‬و ‪ copied_lst‬با یادیگر برابر نمستند‪.‬‬

‫‪ .1-3-6‬نکات کلیدی در هنگام کار با عملگر سوشی‬


‫از عملگر سوشغ (‪ ):‬عالوه بر انتخاب زیرر مزموعرهای از یرک لمسرت‪ ،‬مغتروان بره‬
‫عنوان پاک سازی‪ ،‬برعاس نمودن ترتمب عناصر و پپغ نمودن محتویرات یرک لمسرت‬
‫نمز استفاده نمود‪ .‬البته توجه داشته باشمد په استفاده بمی از حد از این ترفند ممان است‬
‫خوانایغ پد شما را مشال پرده و نگهداری برنامهی نوشته شده سخت شود‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪227 ‬‬

‫‪ .4-6‬آشنایی بیشتر با اشیاء قابل پیمایش‬


‫نوشتن یک حلقه در پایتون در مقایسه با سایر زبانها بسمار ساده اسرت‪ .‬ایرن پرار در‬
‫پایتون همانند نوشتن یک جملهی انگلمسغ است‪.‬‬

‫]‪numbers = [1, 2, 3‬‬


‫‪for n in numbers:‬‬
‫)‪print(n‬‬

‫اما این حلقه در پشت صحنه چگونه پار مغپند؟ چگونه پایتون تشخمص مغدهد په‬
‫یک شغء قابل پممایی با حلقه است یا خمر؟‬
‫در واقع اشمایغ پره دو مترد __‪ __iter‬و __‪ __next‬را پشرتمبانغ پننرد‪ ،‬جزیرغ از‬
‫پروتال ‪ iterator‬در پرایتون حسراب شرده و مغتروان از آنهرا بره عنروان اشرمایغ قابرل‬
‫پممایی با یک حلقه استفاده نمود‪.‬‬
‫در این بخی در مورد نحوهی نوشرتن یرک پرالس برا قابلمرت پشرتمبانغ از پروتارل‬
‫‪ iterator‬در پایتون صحبت مغپنمم‪ .‬بدین صورت مغتوانمم درک عممق تری نسبت به‬
‫حلقهها در پایتون داشته باشمم تا در آینده بتوانمم قدرت برنامره نویسرغ خرود را افرزایی‬
‫دهمم‪.‬‬

‫‪ .1-4-6‬حلقهی بی نهایت‬
‫در این قسمت ابتدا یک پالس ایزاد پرده تا با استفاده از آن پایه و اساس پروتارل‬
‫‪ iterator‬را درک پنمم‪ .‬شاید در ابتدا درک این مفهوم پمغ سخت باشد؛ اما این پرار‬
‫به شناخت شما در مورد پایهای ترین جزیمات زبان پایتون پمک خواهد نمود‪.‬‬
‫در ادامه‪ ،‬یک پالس با نرام ‪ Repeater‬ایزراد خرواهمم پررد پره برا اسرتفاده از ایرن‬
‫پالس مغتوانمم روی یک رشته از متن به صورت بغنهایت همانند صرفحهی بعرد یرک‬
‫حلقهی ‪ for‬را اجرا پنمم‪.‬‬
‫‪  228‬ترفندهای پایتون‬

‫)'‪repeater = Repeater('Hello‬‬
‫‪for item in repeater:‬‬
‫)‪print(item‬‬

‫همانطور په از نام این پالس مشخص است‪ ،‬نمونهی ساخته شردهی ایرن پرالس بره‬
‫تارار مقدار دریافتغ به صورت بغنهایت مغپردازد‪.‬‬
‫برای پماده سازی ایرن قابلمرت‪ ،‬ابتردا پرالس ‪ Repeater‬را بره صرورت زیرر تعریرف‬
‫مغپنمم‪.‬‬

‫‪class Repeater:‬‬
‫‪def __init__(self, value):‬‬
‫‪self.value = value‬‬

‫‪def __iter__(self):‬‬
‫)‪return RepeaterIterator(self‬‬

‫ناتهی قابل توجه در پماده سازی این پالس‪ ،‬نحوهی تعریف مترد __‪ __iter‬در آن‬
‫است‪ .‬پالس ‪ RepeaterIterator‬در واقع یک پالس پماغ است پره بره مرا پمرک‬
‫مغپند تا حلقهی ‪ for‬په در باال نوشتمم‪ ،‬به درستغ اجرا شود‪.‬‬

‫‪class RepeaterIterator:‬‬
‫‪def __init__(self, source):‬‬
‫‪self.source = source‬‬

‫‪def __next__(self):‬‬
‫‪return self.source.value‬‬

‫پالس ‪ RepeaterIterator‬نمز یک پالس ساده است په به صورت باال نوشته شده‬


‫است‪ .‬در این پالس نمز دو متد تعریف شده است‪:‬‬
‫‪ -1‬در متد __‪ __init‬هر نمونه از پالس ‪ RepeaterIterator‬را به شغء ساخته شده‬
‫از پالس ‪ Repeater‬متصل مغپنمم‪ .‬بدین صورت مغتوانمم بره آن شرغ پره از پرالس‬
‫‪ Repeater‬ساخته شده است (‪ )source‬دسترسغ داشته باشمم‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪229 ‬‬

‫‪ -2‬در متد __‪ ،__next‬برا اسرتفاده از ‪ source‬بره مقردار ‪ value‬پره در ‪Repeater‬‬


‫وجود دارد دسترسغ پمدا مغپنمم‪.‬‬
‫در نمونه پد باال‪ ،‬دو پالس ‪ Repeater‬و ‪ RepeaterIterator‬با همااری یاردیگر‬
‫مغتوانند از پروتال ‪ iterator‬در پایتون پشتمبانغ پرده و ایرن عمرل برا پمرک دو مترد‬
‫اصلغ در آنها‪ ،‬یعنغ __‪ __iter‬و __‪ __next‬انزام مغشود‪ .‬در ادامه نگاهغ دقمق ترر‬
‫به نحوهی پار این دو متد خواهمم انداخت‪.‬‬
‫حال از این پالس استفاده پررده و آن را در یرک حلقرهی ‪ for‬مرورد اسرتفاده قررار‬
‫مغدهمم‪ .‬بدین منظور یرک نمونره از پرالس ‪ Repeater‬سراخته و رشرتهی ‪ Hello‬را بره‬
‫عنوان مقدار ورودی این پالس در نظر مغگمریم‪.‬‬

‫)'‪>>> repeater = Repeater('Hello‬‬

‫حال از این شغء به صورت زیر در یک حلقهی ‪ for‬استفاده مغپنمم‪.‬‬

‫‪>>> for item in repeater:‬‬


‫)‪print(item‬‬

‫همانطور په مشخص است‪ ،‬عبارت ‪ Hello‬به مقردار بغنهایرت در صرفحهی نمرایی‬


‫چاپ مغشود‪.‬‬

‫‪Hello‬‬
‫‪Hello‬‬
‫‪Hello‬‬
‫‪Hello‬‬
‫‪Hello‬‬
‫‪...‬‬

‫بدین ترتمب ما توانستمم یک ‪ iterator‬در پایتون بسازیم و از آن در یک حلقهی ‪for‬‬


‫استفاده پنمم‪ .‬به منظور توقف نمایی این پلمه در مفسرر‪ ،‬از پلمردهای ترپمبرغ ‪Ctrl+C‬‬
‫استفاده پنمد‪.‬‬
‫‪  230‬ترفندهای پایتون‬

‫در ادامه به بررسغ دقمق تر این مثال خواهمم پرداخت تا بهتر برا نحروهی پرارپرد دو‬
‫متد __‪ __iter‬و __‪ __next‬آشنا شویم‪.‬‬

‫‪ .2-4-6‬حلقهی ‪ for‬در پایتون به چه صورت کار میکند؟‬


‫بمایمد تا درک پنمم یک حلقهی ‪ for‬در پشت صحنه به چه صورت پار مغپنرد‪ .‬در‬
‫مثال قبل‪ ،‬این حلقه به چه صورت مقدار ورودی را پشت هم چاپ مغنمود؟‬
‫بدین منظور‪ ،‬بهتر است پد نوشتهی شدهی باال را پمغ با جزیمرات بمشرتری بررسرغ‬
‫پنمم و آن را به صورت زیر دوباره نویسغ پنمم‪.‬‬

‫)'‪repeater = Repeater('Hello‬‬
‫)(__‪iterator = repeater.__iter‬‬
‫‪while True:‬‬
‫)(__‪item = iterator.__next‬‬
‫)‪print(item‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬حلقهی ‪ for‬را به ‪ while‬تبدیل نمودیم‪ .‬در ابتدا شرغء‬
‫ایزاد شده از پالس ‪ Repeater‬را برا فراخروانغ مترد __‪ __iter‬از آن‪ ،‬بررای پروتارل‬
‫‪ iterator‬آماده نمرودیم‪ .‬پرس از آن‪ ،‬در حلقرهی ‪ while‬مترد __‪ __next‬را فراخروانغ‬
‫پرده و سپس مقدار حاصل را چاپ پردیم‪.‬‬
‫پالس ‪ Repeater‬یک توالغ از مقادیر را در اختمرار مرا قررار مغدهرد‪ .‬ایرن پرار را‬
‫نمغتوانمم با استفاده از یک لمست در پایتون انزرام دهرمم‪ .‬چررا پره در لمسرت نمغتروان‬
‫تعداد نامتنهایغ از مقادیر قررار داد‪ .‬بردین جهرت ‪iterator‬هرا در پرایتون بسرمار پارآمرد‬
‫هستند‪.‬‬
‫شما چه با لمست یرا دیاشرنری پرار پنمرد و چره برا دنبالرهای از مقرادیر بره صرورت‬
‫بغنهایت (همانند مثال باال) پار پنمد‪ ،‬تمام آنها را مغتوان به صورتغ په گفتره شرد در‬
‫یک حلقهی ‪ while‬نمز پردازش نمایمد‪ .‬همانطور په مشخص است‪ ،‬تنها با فراخروانغ دو‬
‫متد __‪ __iter‬و __‪ __next‬و پار با آنها مغتوان عملمات ‪ iterate‬را انزام داد‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪231 ‬‬

‫شما حتغ مغتوانمد به صرورت دسرتغ نمرز مقرادیر بعردی موجرود در یرک لمسرت یرا‬
‫دیاشنری را به صورت زیر بدست آورید‪ .‬در پد زیر ما این پار را با پالسغ په در باال‬
‫پماده سازی نمودیم انزام دادیم و از تابع ‪ next‬در پایتون استفاده پردیم‪.‬‬

‫)'‪>>> repeater = Repeater('Hello‬‬


‫)‪>>> iterator = iter(repeater‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hello‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hello‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hello‬‬
‫‪...‬‬

‫بدین صورت در هر بار استفاده از تابع ‪ next‬همران پلمرهی ‪ Hello‬بررای مرا نمرایی‬
‫داده مغشود‪ .‬همانطور په مشاهده مغپنمد‪ ،‬در این قطعه پد بزرای اسرتفاده از متردهای‬
‫__‪ __iter‬و __‪ __next‬در پرررالس ‪ ،Repeater‬از توابرررع ‪ next‬و ‪ iter‬در پرررایتون‬
‫استفاده شده است‪ .‬در واقع این توابع همان پرار را بررای مرا انزرام مغدهنرد و از لحراظ‬
‫عملارد تفاوتغ با حالت قبل ندارند؛ فقط در این حالرت‪ ،‬خوانرایغ پرد مرا بهبرود یافتره‬
‫است‪.‬‬
‫این قابلمت برای برخغ دیگر از توابع داخلغ در پایتون نمرز وجرود دارد‪ .‬بررای مثرال‪،‬‬
‫استفاده از )‪ len(x‬دقمقا برابر با فراخوانغ مترد __‪ x.__len‬اسرت‪ .‬اصروال اسرتفاده از ایرن‬
‫توابع در پایتون (بزای فراخوانغ داندر متدها به صورت مستقمم) در هنگرام پرد نویسرغ‬
‫بمشتر پاربرد داشته و به خوانایغ پد شما پمک مغپند‪.‬‬

‫‪ .3-4-6‬پیاده سازی یک کالس ‪ Iterator‬ساده تر‬


‫تا بردین جرا دیردیم پره مثرال پمراده سرازی شرده بررای ‪ iterator‬شرامل دو پرالس‬
‫‪ Repeater‬و ‪ RepeaterIterator‬بود‪ .‬این دو پالس به یادیگر مررتبط بروده و برا هرم‬
‫‪  232‬ترفندهای پایتون‬

‫پار مغپنند‪ .‬اپثر اوقات مغتوان پارایغ این دو پالس را در یک پالس ادررام پررد‪.‬‬
‫به این صورت‪ ،‬پماده سازی پالس ما ساده تر شده و نماز به پد نویسغ پمتری دارد‪.‬‬
‫پالس ‪ RepeaterIterator‬نماز ما به مترد __‪ __next‬را بررای دسرتمابغ بره مقرادیر‬
‫جدید از ‪ iterator‬رفع مغنمود‪ .‬اما در واقع ماان تعریرف ایرن مترد در برنامره‪ ،‬اهممرت‬
‫چندانغ ندارد‪ .‬پروتال ‪ iterator‬تنها به متد __‪ __iter‬نماز دارد په در آن مقادیر را از‬
‫متد __‪ __next‬دریافت پند‪.‬‬
‫در این بخی قصرد داریرم ترا مترد __‪ __next‬را مسرتقمما در داخرل همران پرالس‬
‫‪ Repeater‬تعریف پنمم‪ .‬بدین صورت‪ ،‬نماز مرا بره پرالس ‪ RepeaterIterator‬از برمن‬
‫خواهد رفت و تمام اعمال را در یک پالس پماده سازی مغنمایمم‪.‬‬
‫پالس ساده شدهی ما به صورت زیر خواهد بود‪.‬‬

‫‪class Repeater:‬‬
‫‪def __init__(self, value):‬‬
‫‪self.value = value‬‬

‫‪def __iter__(self):‬‬
‫‪return self‬‬

‫‪def __next__(self):‬‬
‫‪return self.value‬‬

‫این پالس ‪ Repeater‬همچنران مغتوانرد از پروتارل ‪ iterator‬در پرایتون پشرتمبانغ‬


‫پند‪.‬‬

‫)'‪>>> repeater = Repeater('Hello‬‬


‫‪>>> for item in repeater:‬‬
‫)‪print(item‬‬
‫‪Hello‬‬
‫‪Hello‬‬
‫‪Hello‬‬
‫‪...‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪233 ‬‬

‫در واقع در بخیهای ابتدایغ این فصل ما قصد داشتمم این عمل را با پماده سازی دو‬
‫پالس انزام دهمم تا بهتر و دقمق تر مفاهمم را درک پنرمم‪ .‬امرا در ادامره از ایرن پرالس‬
‫استفاده خواهمم نمود‪.‬‬

‫‪ .4-4-6‬چرا این حلقه به صورت بی نهایت کار میکند؟‬


‫تا بدین جا پالس ‪ Repeater‬به صورت برغ نهایرت یرک مقردار دریرافتغ را چراپ‬
‫مغپند‪ .‬در واقع این عمل پارایغ چندانغ در پایتون نردارد‪ .‬اپثرر حلقرهها در پرایتون بره‬
‫صورت زیر پار مغپنند‪.‬‬

‫]‪numbers = [1, 2, 3‬‬


‫‪for n in numbers:‬‬
‫)‪print(n‬‬

‫این پد اعداد ‪ 2 ،1‬و ‪ 3‬موجود در لمست را چراپ پررده و سرپس متوقرف مغشرود‪.‬‬
‫یعنغ دیگر نمازی به فشردن پلمد ترپمبغ ‪ Ctrl+C‬برای متوقف نمودن برنامه و قطع حلقه‬
‫نمست‪.‬‬
‫در این بخی قصد داریم تا یک پالس ‪ iterator‬پماده سازی نمرایمم ترا بره صرورت‬
‫خودپار متوقف شود و به صورت بغ نهایت یک مقردار را در خروجرغ نمرایی ندهرد‪.‬‬
‫بدین منظور پالسغ با نام ‪ BoundedRepeater‬پماده سازی مغپنمم‪ .‬این پرالس بسرمار‬
‫شبمه به همان پالس ‪ Repeater‬در بخی قبل است‪.‬‬
‫اما ایناار باید به چه صورت انزام شرود؟ تشرخمص انتهرای لمسرتغ از مقرادیر توسرط‬
‫پایتون به چه صورت انزام مغشود؟‬
‫بماید به همان قطعه پد قبل نگاهغ دوباره بمندازیم‪ .‬از تابع ‪ iter‬در پایتون برای ایناار‬
‫پمک مغگمریم تا ببمنمم پایتون ‪ iterate‬روی یک لمسرت را بره چره صرورت مردیریت‬
‫مغپند‪.‬‬
‫‪  234‬ترفندهای پایتون‬

‫>>>‬ ‫]‪my_list = [1, 2, 3‬‬


‫>>>‬ ‫)‪iterator = iter(my_list‬‬
‫>>>‬ ‫)‪next(iterator‬‬
‫‪1‬‬
‫>>>‬ ‫)‪next(iterator‬‬
‫‪2‬‬
‫>>>‬ ‫)‪next(iterator‬‬
‫‪3‬‬

‫در قطعه پد باال سه مقدار حاصل در لمست را بدست آوردیرم‪ .‬حرال اگرر دوبراره از‬
‫تابع ‪ next‬استفاده پنمم‪ ،‬چه اتفاقغ خواهد افتاد؟‬

‫)‪>>> next(iterator‬‬
‫‪StopIteration‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬با اینارار خطرای ‪ StopIteration‬بره مرا نمرایی داده‬
‫مغشود‪ .‬بنابراین پروتال ‪ iterator‬از این خطا به منظور تشخمص انتهرای یرک لمسرت و‬
‫مدیریت حلقه استفاده مغپند‪ .‬اگر بازهم از این تابع استفاده پنمم‪ ،‬دوباره هممن خطا بره‬
‫ما نمایی داده مغشود‪.‬‬

‫)‪>>> next(iterator‬‬
‫‪StopIteration‬‬
‫)‪>>> next(iterator‬‬
‫‪StopIteration‬‬
‫‪...‬‬

‫برای بازگرداندن متغمر ‪ iterator‬باید دوباره از لمست خود در تابع ‪ iter‬استفاده پنمم‬
‫تا ایناه بتوانمم تابع ‪ next‬را فراخوانغ نمایمم‪.‬‬
‫حال بمایمرد پرالس ‪ BoundedRepeater‬خرود را بنویسرمم‪ .‬ایرن پرالس عرالوه برر‬
‫دریافت مقدار از ورودی‪ ،‬تعداد دفعات تارار را نمز به عنوان ورودی دریافت مغپند‪.‬‬
235  ‫ حلقهها در پایتون‬:‫فصل ششم‬

class BoundedRepeater:
def __init__(self, value, max_repeats):
self.value = value
self.max_repeats = max_repeats
self.count = 0

def __iter__(self):
return self

def __next__(self):
if self.count >= self.max_repeats:
raise StopIteration
self.count += 1
return self.value

‫ بررای‬.‫ مردیریت مغشرود‬max_requests ‫در پالس براال دفعرات تاررار برا پرارامتر‬
.‫استفاده از این پالس به صورت زیر مغتوانمم عمل پنمم‬

>>> repeater = BoundedRepeater('Hello', 3)


>>> for item in repeater:
print(item)

Hello
Hello
Hello

‫ در پایتون پمراده‬next ‫ و‬iter ‫ و توابع درونغ‬while ‫اگر بخواهمم پد باال را با استفاده‬


.‫ مغتوانمم این قطعه پد را به صورت زیر بازنویسغ پنمم‬،‫سازی نمایمم‬

repeater = BoundedRepeater('Hello', 3)
iterator = iter(repeater)
while True:
try:
item = next(iterator)
except StopIteration:
break
print(item)
‫‪  236‬ترفندهای پایتون‬

‫در قطعه پد باال‪ ،‬در هر بار خطای ‪ StopIteration‬به منظور تشرخمص انتهرای حلقره‬
‫بررسغ مغشود‪.‬‬
‫البته پمشنهاد مغشود بمشتر از حلقههای ‪ for‬بزای ‪ while‬استفاده پنمد‪ .‬چرا په ایناار‬
‫به خوانایغ پد شما پمک مغپند‪.‬‬

‫‪ .5-4-6‬نکات کلیدی در هنگام کار با ‪Iterator‬ها‬


‫از پروتال ‪ Iterator‬به منظور پنترل حلقههای ‪ for‬در پایتون استفاده مغشود‪ .‬بررای‬
‫ایررنپرره پالسررغ از ایررن پروتاررل پشررتمبانغ پنررد‪ ،‬بایررد در پررالس مررورد نظررر دو متررد‬
‫__‪ __iter‬و __‪ __next‬را تعریف پنرمم‪ .‬برا اسرتفاده از ایرن دو مترد مغتروانمم رفترار‬
‫پالس مورد نظر خود را برای پروتال ‪ iterator‬به صورت دلخواه پماده سازی پنمم‪.‬‬
‫البته توجه داشته باشمد په ایرن روش‪ ،‬تنهرا روش موجرود بررای سراخت اشرماء قابرل‬
‫پممایی در پایتون نمست‪ .‬شما مغتوانمد از مولدها نمز برای اینپار استفاده پنمد‪.‬‬

‫‪ .5-6‬مولدها‪ 1‬در پایتون‬


‫تا بدین جای فصل در مورد نحوهی ساخت پالسهایغ با قابلمت پشتمبانغ از پروتال‬
‫‪ iterator‬صحبت پردیم‪ .‬اگر توجه پرده باشمد‪ ،‬ایزاد چنمن پالسهایغ نماز بره پمرغ‬
‫پدنویسغ تاراری و خسته پننده دارد‪ .‬اپثر برنامه نویسان عالقهمند بره نوشرتن پردهای‬
‫تاراری نمستند‪.‬‬
‫همانطور په مغدانمد ‪iterator‬ها در پایتون بسرمار پراربردی بروده پره برا اسرتفاده از‬
‫آنها مغتوانمد با حلقههای ‪ for‬پار پنمد‪ .‬در این بخی یک روش ساده تر بررای ایزراد‬
‫یک ‪ iterator‬را بررسغ مغپنمم‪ .‬این روش نسبت به روش قبل بسمار ساده تر و سریع تر‬
‫بوده و نماز به پدنویسغ پمتری دارد‪ .‬در این بخی از ‪generator‬ها و ‪ yield‬در پرایتون‬
‫استفاده خواهمم پرد‪.‬‬

‫‪1 Generators‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪237 ‬‬

‫‪ .1-5-6‬ایجاد مولدهای بینهایت‬


‫در این بخی قصد داریم نمونه پد پرالس ‪ Repeater‬در بخری قبرل را بره نحروی‬
‫دیگر پماده سازی پنمم‪ .‬اگر به یاد داشته باشمد‪ ،‬پالس ‪ Repeater‬به صورت زیرر پمراده‬
‫سازی شده بود‪.‬‬

‫‪class Repeater:‬‬
‫‪def __init__(self, value):‬‬
‫‪self.value = value‬‬

‫‪def __iter__(self):‬‬
‫‪return self‬‬

‫‪def __next__(self):‬‬
‫‪return self.value‬‬

‫بدیهغ است په برای ایزاد یک ‪ Iterator‬نوشرتن ایرن حزرم از پرد پمرغ برمی از‬
‫اندازه است‪ .‬در این جا به مولدها در پایتون نماز پمدا مغپنمم‪ .‬پماده سازی پالس براال برا‬
‫استفاده از ‪ generator‬به صورت زیر خواهد بود‪.‬‬

‫‪def repeater(value):‬‬
‫‪while True:‬‬
‫‪yield value‬‬

‫همانطور په مشراهده مغپنمرد از یرک پرالس ‪7‬خطرغ بره یرک قطعره پرد ‪3‬خطرغ‬
‫رسمدیم‪ .‬در واقع مولدها همانند تابع در پایتون عمل مغپنند‪ .‬برا ایرن تفراوت پره بزرای‬
‫استفاده از ‪ return‬برای بازگرداندن یک مقدار‪ ،‬از ‪ yield‬استفاده شده است‪.‬‬
‫حال بماید از این مولد در حلقهی ‪ for‬استفاده پنمم‪.‬‬
‫‪  238‬ترفندهای پایتون‬

‫‪>>> for x in repeater('Hi'):‬‬


‫)‪print(x‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫‪...‬‬

‫این مولد همانند پرالس ‪ Repeater‬عمرل مغپنرد و بره همران صرورت نترایج را در‬
‫خروجغ چاپ مغپند‪( .‬توجه داشته باشمد په برای توقف باید از ‪ Ctrl+C‬استفاده پنمد‪).‬‬
‫اما یک مولد در پایتون به چه صورت پار مغپند؟ مولدها از لحاظ ظراهری هماننرد‬
‫یک تابع نوشته مغشوند اما عملارد آنها متفاوت است‪ .‬هنگامغ په یک مولد صدا زده‬
‫مغشود‪ ،‬مقداری به سمت ما برگردانده نمغشود‪ .‬بلاره یرک شرغء از مولرد مرورد نظرر‬
‫ساخته مغشود‪.‬‬

‫)'‪>>> repeater('Hey‬‬
‫>‪<generator object repeater at 0x107bcdbf8‬‬

‫برای بدست آوردن مقداری په ‪ yield‬مغشود باید از تابع ‪ next‬در پرایتون (هماننرد‬
‫آنچه در بخی قبل دیدیم) استفاده پنمم‪.‬‬

‫)'‪>>> generator_obj = repeater('Hey‬‬


‫)‪>>> next(generator_obj‬‬
‫'‪'Hey‬‬

‫در واقع با استفاده از ‪ ،yield‬مولد ما متوقف شده و مقداری را برگردانده و دوباره بره‬
‫پار خود ادامه مغدهد‪.‬‬

‫‪def repeater(value):‬‬
‫‪while True:‬‬
‫‪yield value‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪239 ‬‬

‫هنگامغ په در یک تابع از ‪ return‬استفاده شده و یک مقدار برگشت داده مغشود‪،‬‬


‫پنترل برنامه بطور دائم به صدا زننردهی ترابع منتقرل مغشرود‪ .‬امرا هنگرامغ پره از ‪yield‬‬
‫استفاده مغشود‪ ،‬پنترل برنامه بطور موقت به صدا زنندهی مولد منتقل مغگردد‪ .‬امرا ایرن‬
‫به چه معناست؟‬
‫درحالغ په ‪ return‬وضعمت یک تابع را پرس از برگشرت دادن یرک مقردار از برمن‬
‫مغبرد‪ yield ،‬یک مولرد را بره حالرت تعلمرق در آورده و وضرعمت آن را در خرود نگره‬
‫مغدارد‪ .‬به عبارت دیگر متغمرهای محلغ در مولد و وضعمت اجرای برنامه در آن به طور‬
‫موقت از بمن رفته و به بمرون درز پمدا نمغپند‪ .‬روند اجرا مغتواند با فراخوانغ دوبرارهی‬
‫تابع ‪ next‬روی مولد ادامه یابد‪.‬‬

‫)'‪>>> iterator = repeater('Hi‬‬


‫)‪>>> next(iterator‬‬
‫'‪'Hi‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hi‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hi‬‬

‫این قابلمت باعث مغشود تا مولدها بطور پامل بتوانند از پروتال ‪ iterator‬در پایتون‬
‫پشتمبانغ پنند‪ .‬به هممن دلمل پمشنهاد مغشود ترا بزرای نوشرتن پالسهرایغ طروالنغ‪ ،‬از‬
‫مولد ها بدین منظور استفاده پنمد‪.‬‬

‫‪ .2-5-6‬پیاده سازی مولدهایی که بطور خودکار متوقف میشوند‬


‫در بخی قبل یک مولد پماده سازی نمودیم په به صورت بغ نهایرت یرک مقردار را‬
‫در خروجغ چاپ مغنمود‪ .‬در ادامه قصد داریم مولدی بنویسمم په پس از مدتغ چراپ‬
‫در خروجغ را متوقف پند‪.‬‬
‫اگر به یاد داشته باشمد‪ ،‬زمانغ په این پار را با پمک نوشتن یک پالس پماده سازی‬
‫نمودیم‪ ،‬از خطای ‪ StopIteration‬به منظور متوقف پردن رونرد اجررای حلقره اسرتفاده‬
‫‪  240‬ترفندهای پایتون‬

‫پردیم‪ .‬در واقع در مولدها نمز هممن عمل در پشت صحنه رخ مغدهد‪ ،‬اما نمرازی نمسرت‬
‫تا ما آن را در پد خود به صورت دسرتغ بنویسرمم‪ .‬هنگرامغ پره مقرداری برا اسرتفاده از‬
‫‪ yield‬از یک مولد برگردانده نشود‪ ،‬بطور خودپار روند آن متوقف مغشود‪.‬‬

‫‪def repeat_three_times(value):‬‬
‫‪yield value‬‬
‫‪yield value‬‬
‫‪yield value‬‬

‫در این مولد از ‪ while‬استفاده نشده و به صرورت بسرمار سراده تنهرا سره برار از ‪yield‬‬
‫استفاده شده است‪ .‬پس از فراخوانغ مولد‪ ،‬هر پدام از ‪yield‬ها طبق روال مقداری را بره‬
‫صدا زننده برمغگردانند‪ .‬هنگامغ پره بره انتهرای مولرد برسرمم‪ ،‬رونرد اجررای آن بطرور‬
‫خودپار متوقف خواهد شد‪.‬‬

‫‪>>> for x in repeat_three_times('Hey there'):‬‬


‫)‪print(x‬‬
‫'‪'Hey there‬‬
‫'‪'Hey there‬‬
‫'‪'Hey there‬‬

‫این مولد پس از ‪3‬بار نمایی مقدار دریافتغ متوقف خواهد شد‪ .‬ایرن عمرل در پشرت‬
‫صحنه با همان خطای ‪ StopIteration‬پنترل مغشود‪ .‬برای اطممنران بره قطعره پرد زیرر‬
‫نگاهغ مغاندازیم‪.‬‬

‫)'‪>>> iterator = repeat_three_times('Hey there‬‬


‫)‪>>> next(iterator‬‬
‫'‪'Hey there‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hey there‬‬
‫)‪>>> next(iterator‬‬
‫'‪'Hey there‬‬
‫)‪>>> next(iterator‬‬
‫‪StopIteration‬‬
‫)‪>>> next(iterator‬‬
‫‪StopIteration‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪241 ‬‬

‫پس از ‪ 3‬بار فراخوانغ تابع ‪ ،next‬دیگر مقداری از مولد ‪ yield‬نخواهد شد و پرس از‬
‫آن خطای ‪ StopIteration‬به ما نمایی داده مغشود‪.‬‬
‫بمایمد دوباره نگاهغ به پالس ‪ BoundRepeater‬در بخی قبل بمندازیم‪ .‬این پالس‬
‫عالوه بر دریافت یک مقدار به منظور نمایی‪ ،‬تعرداد دفعرات تاررار آن را نمرز دریافرت‬
‫مغنمود‪.‬‬

‫‪class BoundedRepeater:‬‬
‫‪def __init__(self, value, max_repeats):‬‬
‫‪self.value = value‬‬
‫‪self.max_repeats = max_repeats‬‬
‫‪self.count = 0‬‬

‫‪def __iter__(self):‬‬
‫‪return self‬‬

‫‪def __next__(self):‬‬
‫‪if self.count >= self.max_repeats:‬‬
‫‪raise StopIteration‬‬
‫‪self.count += 1‬‬
‫‪return self.value‬‬

‫اگر بخواهمم این پالس را با استفاده از یک مولد پماده سازی پنرمم‪ ،‬مغتروانمم پرد‬
‫آن را به صورت زیر بنویسمم‪.‬‬

‫‪def bounded_repeater(value, max_repeats):‬‬


‫‪count = 0‬‬
‫‪while True:‬‬
‫‪if count >= max_repeats:‬‬
‫‪return‬‬
‫‪count += 1‬‬
‫‪yield value‬‬

‫در قطعه پد باال به صورت عمدی از ‪ return‬در حلقهی ‪ while‬خود استفاده پردیم‬
‫تا پس از تعداد ‪ max_repeats‬خطای ‪ StopIteration‬را دریافت پنرمم‪ .‬در ادامره ایرن‬
‫‪  242‬ترفندهای پایتون‬

‫مولد را به صورت ساده تر پماده سازی مغپنمم‪ .‬اما ابتدا بمایمرد نگراهغ بره عملاررد ایرن‬
‫برنامه بمندازیم‪.‬‬

‫‪>>> for x in bounded_repeater('Hi', 4):‬‬


‫)‪print(x‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬
‫'‪'Hi‬‬

‫بدین صورت یک مولد ایزاد نمودیم په برا دریافرت تعرداد تاررار‪ ،‬یرک مقردار را‬
‫نمایی مغدهد‪ .‬برای نمایی مقدار دریافتغ از ‪ yield‬استفاده شده است و پرس از تعرداد‬
‫‪ max_request‬از ‪ return‬استفاده شده است په دیگر چمزی نمایی داده نشده و حلقه‬
‫متوقف خواهد شد‪.‬‬
‫همانطور په گفته بودیم‪ ،‬قصد داریم تا این مولرد را بره صرورتغ سراده ترر نمرز پمراده‬
‫سازی پنمم‪ .‬مغدانمد په پایتون به صورت ضمنغ عبارت ‪ return None‬را در انتهای هر‬
‫ترابع قررار مغدهررد‪ .‬مرا از ایررن قابلمرت اسررتفاده پررده و مولررد خرود را برره صرورت زیررر‬
‫مغنویسمم‪.‬‬

‫‪def bounded_repeater(value, max_repeats):‬‬


‫‪for i in range(max_repeats):‬‬
‫‪yield value‬‬

‫قطعررره پرررد بررراال هماننرررد مولرررد قبلرررغ عمرررل مغپنرررد‪ .‬مرررا از پرررالس ‪12‬خطرررغ‬
‫‪ BoundedRepeater‬به یک مولد ‪3‬خطغ رسمدیم په دقمقا همان قابلمت را دارد‪.‬‬
‫استفاده از مولدها در برنامه نویسغ‪ ،‬به پد نویسرغ شرما پمرک زیرادی مغپنرد و برا‬
‫استفاده از آنها مغتوانمد پدهایغ ساده تر و تممز تر پماده سازی پنمد‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪243 ‬‬

‫‪ .3-5-6‬نکات کلیدی در هنگام کار با مولدها در پایتون‬


‫از مولدها مغتوان به عنوان ‪ Iterator‬در پرایتون اسرتفاده نمرود‪ .‬آنهرا بره مرا پمرک‬
‫مغپننرد پرره دیگرر نمررازی بره نوشررتن پالسهرایغ طرروالنغ بررای پشررتمبانغ از پروتاررل‬
‫‪ Iterator‬نداشته باشمم‪.‬‬
‫با استفاده از ‪ yield‬مغتوان روند اجرای برنامه را به صورت موقت در مولرد متوقرف‬
‫پرده و مقداری را به صدا زننده بازگرداند‪ .‬پس از آن په دیگر عبرارت ‪ yield‬در یرک‬
‫مولد وجود نداشته باشد‪ ،‬خطای ‪ StopIteration‬به صورت خودپار رخ مغدهد په این‬
‫به معنای انتهای پار حلقه خواهد بود‪.‬‬

‫‪ .6-6‬آشنایی بیشتر با مولدها‬


‫همانطور په در بخی قبل مشاهده پردید‪ ،‬دریافتمم په پالسها و مولردهایغ پره از‬
‫پروتال ‪ iterator‬پشتمبانغ مغپنند‪ ،‬از یک الگوی طراحغ‪ 1‬پمروی مغپنند‪ .‬اسرتفاده از‬
‫مولدها باعث مغشود تا نماز به پدنویسغ پاهی یابد و سریع تر به هدف خود برسمم‪.‬‬
‫از آنزا په بسماری از برنامه نویسان از ایرن الگروی طراحرغ پمرروی مغپننرد‪ ،‬خرالق‬
‫زبانهای برنامه نویسغ به این فار افتادند تا راههایغ ساده تر و سریع تر برای پماده سازی‬
‫همچمن قابلمتغ برای برنامه نویسان به وجود آورند‪ .‬بره همرمن دلمرل اسرت پره زبانهرای‬
‫برنامه نویسغ در طول زمان بهتر مغشوند و برنامه نویسان نمز باید هممشره برروز باشرند ترا‬
‫بتوانند از اماانات مفمد زبان برنامه نویسغ خود استفاده پنند‪.‬‬
‫در بخی قبل با نحوهی پماده سازی مولدها آشنا شدیم‪ .‬در این بخی یک گام بمشتر‬
‫رو به جلو برداشته و پماده سازی قبل را ساده تر مغنمایمم‪ .‬این عبارات دقمقا همانند پماده‬
‫سازی یک لمست در یک خط است (په در بخیهای قبل با آن آشرنا شردیم)؛ تنهرا برا‬
‫این تفاوت په بزای استفاده از براپت [] از پرانتز () استفاده مغپنمم‪ .‬در ادامره نحروهی‬
‫پماده سازی مولد در یک خط نشان داده شده است‪.‬‬

‫‪1 Design Pattern‬‬


‫‪  244‬ترفندهای پایتون‬

‫))‪iterator = ('Hello' for i in range(3‬‬

‫این عبارت تک خطغ دقمقرا همران پراری را بررای مرا انزرام مغدهرد پره در مولرد‬
‫‪ bounded_repeater‬پماده سازی پرده بودیم‪ .‬در زیر برای یادآوری این متد را دوباره‬
‫مغتوانمد ببمنمد‪.‬‬

‫‪def bounded_repeater(value, max_repeats):‬‬


‫‪for i in range(max_repeats):‬‬
‫‪yield value‬‬

‫)‪iterator = bounded_repeater('Hello', 3‬‬

‫همانطور په مشاهده مغپنمد توانستمم هممن پار را تنها با نوشتن یک خط پد انزام‬


‫دهمم‪ .‬در قطعه پد زیر از مولدی په با یک خط ایزاد پرده بودیم استفاده مغنمایمم ترا‬
‫مطمئن شویم به درستغ پار مغپند‪.‬‬

‫))‪>>> iterator = ('Hello' for i in range(3‬‬


‫‪>>> for x in iterator:‬‬
‫)‪print(x‬‬
‫'‪'Hello‬‬
‫'‪'Hello‬‬
‫'‪'Hello‬‬

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

‫‪ .1-6-6‬تفاوت ساخت مولد و ساخت لیست در یک خط‬


‫مطمئنا تا بدین جا توجه پردهاید په ساخت مولد در یک خط بسمار شربمه بره ایزراد‬
‫یررک لمسررت اسررت‪ .‬در قطعرره پررد صررفحهی بعررد برره ایررن دو در پنررار یارردیگر نگرراهغ‬
‫مغاندازیم‪.‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪245 ‬‬

‫])‪>>> listcomp = ['Hello' for i in range(3‬‬


‫))‪>>> genexpr = ('Hello' for i in range(3‬‬

‫برخالف لمست‪ ،‬ساخت یک مولد بدین صورت اشمایغ از عناصر به صرورت لمسرت‬
‫نمغسازد و تنها مقادیری را به صورت لحظهای و یابار مصرف ایزراد مغپنرد (هماننرد‬
‫مولدهایغ په به صورت تابع یا پالس در بخیهای قبل مغساختمم)‪.‬‬
‫در صورتغ په بخواهمم محتویات این متغمرها را مشاهده پنمم‪ ،‬خروجرغ زیرر بره مرا‬
‫نمایی داده مغشود‪.‬‬

‫‪>>> listcomp‬‬
‫]'‪['Hello', 'Hello', 'Hello‬‬
‫‪>>> genexpr‬‬
‫>‪<generator object <genexpr> at 0x1036c3200‬‬

‫همانند بخی قبل‪ ،‬برای دسترسغ به مقرادیر ایرن مولرد بایرد از ترابع ‪ next‬در پرایتون‬
‫استفاده پنمم‪.‬‬

‫)‪>>> next(genexpr‬‬
‫'‪'Hello‬‬
‫)‪>>> next(genexpr‬‬
‫'‪'Hello‬‬
‫)‪>>> next(genexpr‬‬
‫'‪'Hello‬‬
‫)‪>>> next(genexpr‬‬
‫‪StopIteration‬‬

‫همچنمن مغتوانمد از تابع ‪ list‬روی یک مولد استفاده نمود ترا آن را بره یرک لمسرت‬
‫تبدیل پند‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫))‪>>> genexpr = ('Hello' for i in range(3‬‬


‫)‪>>> list(genexpr‬‬
‫]'‪['Hello', 'Hello', 'Hello‬‬

‫به صورت باال مغتوان به راحتغ یک مولد را بره لمسرت تبردیل نمرود‪ .‬البتره پمشرنهاد‬
‫مغشود برای ساخت یک لمست در یک خرط از همران روش قبرل اسرتفاده پنمرد‪ .‬اگرر‬
‫‪  246‬ترفندهای پایتون‬

‫بخواهمم به طور پلغ به الگوی چگونگغ ساخت یک مولد در یک خط دست یابمم‪ ،‬برا‬
‫پمغ بررسغ پدهای باال مغتوان الگویغ همانند زیر نوشت‪.‬‬

‫)‪genexpr = (expression for item in collection‬‬

‫خط باال را مغتوان همانند یک تابع نمز برای ایزاد مولد به صرورت زیرر نوشرت پره‬
‫این دو دقمقا به یک صورت عمل مغپنند‪.‬‬

‫‪def generator():‬‬
‫‪for item in collection:‬‬
‫‪yield expression‬‬

‫بدین ترتمب مغتوان هر مولدی په به صرورت ترابع نوشرته شرده اسرت را برا پمرک‬
‫الگوی باال‪ ،‬در یک خط پماده سازی نمود‪.‬‬

‫‪ .2-6-6‬فیلتر کردن مقادیر در هنگام ساخت مولد‬


‫ما مغتوانمم با استفاده از الگوی باال‪ ،‬خاصمت شرط ها را نمز در هنگام سراخت مولرد‬
‫نمز اعمال پنمم‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫)‪>>> even_squares = (x * x for x in range(10‬‬


‫)‪if x % 2 == 0‬‬

‫مولد باال مربع تمام اعداد زوج بمن ‪ 0‬ترا ‪ 9‬را شرامل مغشرود‪ .‬همرانطور پره مشراهده‬
‫مغپنمد‪ ،‬در شرط این مولد خاصمت زوج بودن اعداد مورد نظر بررسرغ شرده و تنهرا از‬
‫آنها استفاده مغشود‪.‬‬

‫‪>>> for x in even_squares:‬‬


‫)‪print(x‬‬
‫‪0 4‬‬
‫‪16‬‬
‫‪36‬‬
‫‪64‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪247 ‬‬

‫حال مغتوانمم الگوی قبل را بروز رسانغ پرده و خاصمت بررسغ شرط و فملتر پردن‬
‫مقادیر را نمز در آن لحاظ پنمم‪.‬‬

‫‪genexpr = (expression for item in collection‬‬


‫)‪if condition‬‬

‫الگوی باال دقمقا همانند الگوی ساخت یک مولد به صورت تابع است‪ .‬همانند حالت‬
‫قبل‪ ،‬این دو از لحاظ عملارد به یک صورت هستند‪.‬‬

‫‪def generator():‬‬
‫‪for item in collection:‬‬
‫‪if condition:‬‬
‫‪yield expression‬‬

‫خط‪1‬‬ ‫‪ .3-6-6‬عبارات مولد بر‬


‫از چنمن مولدهایغ مغتوان به همراه سایر عبارات نمز در یک خط استفاده نمود‪ .‬برای‬
‫مثال مغتوانمم یک مولد را در یک خط بره همرراه حلقرهی ‪ for‬نوشرته ترا مقرادیر آن را‬
‫بالفاصله در خروجغ نمایی دهمم‪.‬‬

‫‪for x in ('Bom dia' for i in range(3)):‬‬


‫)‪print(x‬‬

‫همچنمن اگر بخواهمم مقادیر یک مولد را بره عنروان آرگومران بره یرک ترابع دهرمم‪،‬‬
‫مغتوانمم از نوشتن پرانتز برای آن صرف نظر پنمم تا پد ما زیبا تر و سراده ترر شرود‪ .‬بره‬
‫مثال زیر توجه پنمد‪:‬‬

‫)))‪>>> sum((x * 2 for x in range(10‬‬


‫‪90‬‬

‫‪# Versus:‬‬

‫))‪>>> sum(x * 2 for x in range(10‬‬


‫‪90‬‬

‫‪1 In-line Generator Experssions‬‬


‫‪  248‬ترفندهای پایتون‬

‫بدین صورت پد نویسغ ما ساده تر مغشود‪ .‬توجه داشته باشمد پره مولرد هرا فضرای‬
‫حافظهی بسمار پمغ اشغال مغپننرد‪ .‬چررا پره مقرادیر درون آن پرس از یابرار اسرتفاده‬
‫بالفاصله از بمن مغروند‪ .‬بدین ترتمب برای بسماری از برنامههای په مغخواهمم بنویسمم‪،‬‬
‫استفاده از مولدها مغتواند مفمد واقع شود‪.‬‬

‫‪ .4-6-6‬عدم استفادهی بیش از حد برای ساخت مولد در یک خط‬


‫مولدهایغ په در یک خط نوشته مغشوند‪ ،‬ممان است گاهغ بسرمار پمچمرده شروند‪.‬‬
‫برای مثال مغتوان چندین حلقهی تو در تو و چندین شررط را بره صرورتغ پره در ادامره‬
‫مغبمنمد برای ساخت یک مولد پماده سازی نمود‪.‬‬

‫‪(expr for x in xs if cond1‬‬


‫‪for y in ys if cond2‬‬
‫‪...‬‬
‫)‪for z in zs if condN‬‬

‫عبارت باال را مغتوان به صورت یک تابع مولدی همانند زیر نمز پماده سازی نمود‪.‬‬

‫‪for x in xs:‬‬
‫‪if cond1:‬‬
‫‪for y in ys:‬‬
‫‪if cond2:‬‬
‫‪...‬‬
‫‪for z in zs:‬‬
‫‪if condN:‬‬
‫‪yield expr‬‬

‫پمشنهاد مغشود به همک وجه چنمن حلقههایغ را به صورت یک عبرارت ترک خطرغ‬
‫ننویسمد‪ .‬چرا په درک برنامرهی شرما بررای سرایر برنامره نویسران بسرمار سرخت بروده و‬
‫نگهداری چنمن پدهایغ بسمار دشوار است‪.‬‬
‫هممشه سعغ پنمد تا از عبارات تک خطغ برای موارد ساده استفاده پنمد‪ .‬بهتر اسرت‬
‫در صورتغ په نماز است تا دو حلقهی تو در ترو در یرک مولرد ایزراد پنمرد‪ ،‬از نوشرتن‬
‫عبارت تک خطغ برای ساخت مولد پرهمرز پررده و بزرای آن از روش ترابعغ اسرتفاده‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪249 ‬‬

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

‫‪ .5-6-6‬نکات کلیدی در هنگام ایجاد مولدها در یک خط‬


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

‫زنجیرهای‪1‬‬ ‫‪ .7-6‬ساخت مولدها به صورت‬


‫در این بخی قصد داریم تا یاغ از دیگر از ویژگغهای ‪iterator‬ها را بررسغ پنمم‪.‬‬
‫با به هم پموستن چندین ‪ iterator‬ساده در پایتون به صرورت زنزمررهای‪ ،‬مغتروان یرک‬
‫مولد پمچمده تر و طوالنغ تر ایزاد نمود‪.‬‬
‫در این بخی ابتدا این روش را با نوشتن مولردهای ترابعغ پمراده سرازی مغنمرایمم ترا‬
‫روش پار این ویژگغ را بهتر درک پنمم و در ادامه این مولدها را به صورت تک خطغ‬
‫مغنویسمم‪.‬‬

‫‪1 Chain‬‬
‫‪  250‬ترفندهای پایتون‬

‫در قطعه پد زیر دنبالهای از اعداد را از ‪ 1‬تا ‪ 8‬در یک مولد تابعغ ایزاد نمودیم‪.‬‬

‫‪def integers():‬‬
‫‪for i in range(1, 9):‬‬
‫‪yield i‬‬

‫حال از این مولد استفاده پررده و مقرادیر خروجرغ آن را برا اسرتفاده از ترابع ‪ list‬در‬
‫پایتون‪ ،‬به لمستغ از مقادیر تبدیل مغپنمم‪.‬‬

‫)(‪>>> chain = integers‬‬


‫)‪>>> list(chain‬‬
‫]‪[1, 2, 3, 4, 5, 6, 7, 8‬‬

‫در ادامه قصد داریم تا مولدی دیگر بسازیم په این مولد دنبالهای از اعداد را گرفته و‬
‫مربع آنها را به ما برمغگرداند‪.‬‬

‫‪def squared(seq):‬‬
‫‪for i in seq:‬‬
‫‪yield i * i‬‬

‫این دو مولد مغتوانند به یاردیگر بره صرورت زنزمررهای وصرل شرده و دنبالرهای از‬
‫پردازشها را انزام دهند و یک خروجغ واحد را به ما نمایی دهند‪ .‬تنها پافغ اسرت ترا‬
‫مولد ‪ integers‬را به ‪ squared‬پاس دهمم و خروجغ آنهرا را بره صرورت یرک لمسرت‬
‫مشاهده نمایمم‪ .‬در قطعه پد زیر برای صحت عملارد این دو مولد به صورت زنزمرهای‬
‫این پار را انزام دادهایم‪.‬‬

‫))(‪>>> chain = squared(integers‬‬


‫)‪>>> list(chain‬‬
‫]‪[1, 4, 9, 16, 25, 36, 49, 64‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬لمست نمایی داده شده برابر با مربع اعداد ‪ 1‬تا ‪ 8‬است‪.‬‬
‫در واقع ما یک خط لوله‪ 1‬برای پردازش پشت هم دو مولد ایزاد پردهایم‪ .‬مفهروم خرط‬

‫‪1 pipeline‬‬
‫فصل ششم‪ :‬حلقهها در پایتون ‪251 ‬‬

‫لوله در اینزا‪ ،‬در سمستمهای ‪ Unix‬نمرز بارار بررده مغشرود پره چنردین پرردازش را بره‬
‫یارردیگر مرررتبط پرررده و خروجررغ هرپرردام را برره عنرروان ورودی دسررتور بعررد در نظررر‬
‫مغگمرند‪.‬‬
‫در ادامه قصد داریم تا یک مولد دیگر به خط لولهی پردازشهای خود اضافه پنرمم‪.‬‬
‫این مولد تمام مقادیر موجود در یک دنباله را منفغ مغنماید‪.‬‬

‫‪def negated(seq):‬‬
‫‪for i in seq:‬‬
‫‪yield -i‬‬

‫حال اگر این سه مولد را به صورت زنزمرهای به یادیگر متصل پنمم‪ ،‬خروجغ زیرر‬
‫را خواهمم داشت‪.‬‬

‫)))(‪>>> chain = negated(squared(integers‬‬


‫)‪>>> list(chain‬‬
‫]‪[-1, -4, -9, -16, -25, -36, -49, -64‬‬

‫توجه داشته باشمد په تنها یک مقدار در هر لحظه برای پردازش مغتواند در این خط‬
‫لوله قرار گمرد و همک دادهای بافر نمغشود‪ .‬در مرحلهی اول مولد ‪ integers‬یک مقردار‬
‫را تولمد مغپند (برای مثال عردد ‪)3‬؛ سرپس مولرد ‪ squared‬فعرال شرده و ایرن عردد را‬
‫پردازش پرده و مربع آن یعنغ ‪ 9‬را آماده مغنماید؛ در مرحلهی آخر عدد ‪ 9‬حاصرل بره‬
‫مولد ‪ negated‬برای پردازش فرستاده شده و عدد ‪ 9-‬بازگردانده مغشود‪.‬‬
‫شما مغتوانمد با استفاده از این روش مولدهای پمچمده ترری را برا اسرتفاده از چنردین‬
‫مولد پوچک و ساده پماده سازی پنمد‪ .‬استفاده از این روش بسرمار مناسرب اسرت‪ .‬چررا‬
‫په در آینده برای تغممر بخشغ از برنامهی خود تنهرا پرافغ اسرت همران مولرد پوچرک‬
‫مورد نظر را تغممر دهمد و از پمچمدگغهای بمشتر دوری نمایمد‪.‬‬
‫‪  252‬ترفندهای پایتون‬

‫ما مغتوانمم تمام این مولدها را به صورت تک خطغ نمز پماده سازی پنمم پره دقمقرا‬
‫همان خروجغ را برای ما نمایی دهد‪ .‬در قطعه پد زیر نحوهی پماده سازی این سه مولد‬
‫را مغتوانمد مشاهده پنمد‪.‬‬

‫)‪integers = range(8‬‬
‫)‪squared = (i * i for i in integers‬‬
‫)‪negated = (-i for i in squared‬‬

‫توجه داشته باشمد په تمام پدهای باال تنها در هممن سه خرط خالصره شردهاند‪ .‬اگرر‬
‫بخواهمم خروجغ مولد ‪ negated‬را مشاهده پنمم‪ ،‬مغتوانمم از تابع ‪ list‬استفاده نمایمم‪.‬‬

‫‪>>> negated‬‬
‫>‪<generator object <genexpr> at 0x1098bcb48‬‬
‫)‪>>> list(negated‬‬
‫]‪[0, -1, -4, -9, -16, -25, -36, -49‬‬

‫تنها نقطه ضعف استفاده از این روش این است په آنها را نمغتوان با آرگومانهایغ‬
‫دیگر استفاده نمود‪ .‬پس از یابار استفاده از این مولدها‪ ،‬آنها پامالد از بمن رفتره و بررای‬
‫استفادهی مزدد از آنها باید دوباره این مولدها را ایزراد نمرایمم‪ .‬بنرابراین توجره داشرته‬
‫باشمد په بسته به نماز خود در جای مناسب از چنمن مولدهایغ استفاده پنمد‪.‬‬

‫‪ .1-7-6‬نکات کلیدی در هنگام ساخت مولدهای زنجیرهای‬


‫مولدها را مغتوان همانند خط لوله به صورت زنزمرهای به منظور پردازش دادهها بره‬
‫یادیگر متصل پرده و یک مولد بزرگتر ایزاد نمود‪ .‬برای تغممر هر بخی از ایرن مولرد‬
‫تنها پافغ است تا زیر مولد آن را تغممر دهمم‪.‬‬
‫همچنمن برای راحتغ در پد نویسغ مغتوان تمام آنها را به صورت تک خطغ پماده‬
‫سازی پرد‪ .‬البته توجه داشته باشمد په استفاده بمی از حد از این روش باعث مغشود ترا‬
‫از خوانایغ برنامهی شما پاسته شود و پار را برای سایر برنامه نویسان سخت تر نماید‪.‬‬
‫فصل هفتم‪ :‬ترفندهایغ برای پار با دیاشنریها‬

‫فصل هفتم‬

‫ترفندهایی برای کار با‬


‫دیکشنریها‬
‫‪ .1-7‬مقادیر پیش فرض در دیکشنریها‬
‫یاغ از متدهای پر پاربرد در پایتون برای دیاشنریها‪ ،‬متد ‪ get‬اسرت‪ .‬ایرن مترد بره‬
‫دنبال یک پلمد در دیاشنری گشته و در صورت نبرود آن‪ ،‬یرک مقردار پرمی فررض را‬
‫برای ما برمغگرداند‪ .‬استفاده از این متد به منظور جلوگمری از برخرغ خطاهرا در برنامره‬
‫نویسغ بسمار مناسب است‪ .‬در ادامه مثالغ را در این مورد بررسغ خرواهمم نمرود‪ .‬فررض‬
‫پنمد په یک دیاشنری به صورت زیر داریم په شمارهی هر پاربر به عنوان پلمد و نام‬
‫پاربر به عنوان مقدار آن پلمد در نظر گرفته شده است‪.‬‬

‫{ = ‪name_for_userid‬‬
‫‪382: 'Alice',‬‬
‫‪950: 'Bob',‬‬
‫‪590: 'Dilbert',‬‬
‫}‬

‫حال قصد داریم ترا از ایرن دیاشرنری اسرتفاده پررده و یرک ترابع برا نرام ‪greeting‬‬
‫بنویسمم‪ .‬این تابع شمارهی هر پاربر را دریافت پرده و پمام خوش آمدی را به آن پاربر‬
‫نمایی مغدهد‪ .‬پماده سازی اولمه این تابع به صورت زیر است‪.‬‬

‫‪def greeting(userid):‬‬
‫]‪return 'Hi %s!' % name_for_userid[userid‬‬

‫ندارد‬ ‫تابع باال بسمار ساده نوشته شده است‪ .‬اما اگر شمارهای په به پاربری اختصا‬
‫را به این تابع بدهمم‪ ،‬برنامهی ما دچار خطا مغشود‪.‬‬

‫)‪>>> greeting(382‬‬
‫'!‪'Hi Alice‬‬

‫)‪>>> greeting(33333333‬‬
‫‪KeyError: 33333333‬‬
‫‪  256‬ترفندهای پایتون‬

‫خطای ‪ KeyError‬چمزی نمست په ما بخواهمم در برنامهی خود مشراهده پنرمم‪ .‬بهترر‬


‫است تابع خود را طوری پماده سازی نمایمم تا در صورت عدم وجود یک شماره‪ ،‬دچرار‬
‫خطا نشده و یک پمام پمی فرض را بزای خطا به ما نمایی دهد‪.‬‬
‫بدین منظور از شرط ‪ if‬در برنامهی خود استفاده مغپنمم تا اگر شمارهی ارسال شرده‬
‫به تابع در دیاشنری مورد نظر ما وجود نداشت‪ ،‬یک پمغرام خطرای ابرت برگشرت داده‬
‫شود‪.‬‬

‫‪def greeting(userid):‬‬
‫‪if userid in name_for_userid:‬‬
‫]‪return 'Hi %s!' % name_for_userid[userid‬‬
‫‪else:‬‬
‫'!‪return 'Hi there‬‬

‫حال اگر از این تابع استفاده پنرمم‪ ،‬خروجغهرایغ بصرورت زیرر بره مرا نمرایی داده‬
‫مغشود‪.‬‬

‫)‪>>> greeting(382‬‬
‫'!‪'Hi Alice‬‬

‫)‪>>> greeting(33333333‬‬
‫'!‪'Hi there‬‬

‫بدین ترتمب پارایغ برنامه ی ما بهتر شده است‪ .‬از این پس اگر عددی را به این ترابع‬
‫بدهمم په متعلق به همک پاربری نباشد‪ ،‬پمغامغ پمی فرض برای ما نمایی داده مغشرود‪.‬‬
‫اما هنوز هم مغتوان پارایغ این برنامه را بهبود بخشمد‪ .‬قطعه پرد براال بره دالیرل زیرر از‬
‫لحاظ پارایغ ضعمف مغباشد‪:‬‬
‫‪ ‬عمل جست و جو در دیاشنری در این تابع دو بار انزام مغشود (یابار در شرط‬
‫‪ if‬و بار دیگر در هنگام جست و جو برای شمارهی پاربر در هنگام نمایی پمام)‬
‫‪ ‬پمام ‪ Hi‬یابار در ‪ if‬و بار دیگر در ‪ else‬نوشته شده است‪ .‬بهتر است تا ایرن دو برا‬
‫هم ادرام شوند‪.‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪257 ‬‬

‫‪ ‬همچنمن نوشتن چنمن پدی از لحاظ پایتونغ صحمح نمست‪ .‬چرا پره در صرورت‬
‫نبود پلمدی برای یک دیاشنری‪ ،‬بهتر است تا این خطا با ‪ try...except‬مدیریت‬
‫شود‪.‬‬
‫در ادامه‪ ،‬پد براال را بره نروعغ دیگرر پمراده سرازی مرغپنمم‪ .‬ایرن برار برا اسرتفاده از‬
‫‪ try...except‬خطای احتمالغ را مدیریت خواهمم نمود‪.‬‬

‫‪def greeting(userid):‬‬
‫‪try:‬‬
‫]‪return 'Hi %s!' % name_for_userid[userid‬‬
‫‪except KeyError:‬‬
‫'‪return 'Hi there‬‬

‫در قطعه پد باال دیگر عمل جست و جو در دیاشنری ‪2‬بار انزام نمغشود‪.‬‬
‫اما باز هم مغتوانمم این پد را بهبود ببخشرمم و آن را تممرز ترر پمراده سرازی نمرایمم‪.‬‬
‫دیاشنریها در پایتون دارای متدی با نام ‪ get‬هسرتند‪ .‬برا اسرتفاده از ایرن مترد مغتروانمم‬
‫مقداری پمی فرض را برای یک دیاشنری در نظر بگمرریم ترا در صرورت عردم وجرود‬
‫یک پلمد در دیاشنری‪ ،‬مقدار پمی فرض برای پاربر نمایی داده شود‪.‬‬

‫‪def greeting(userid):‬‬
‫(‪return 'Hi %s!' % name_for_userid.get‬‬
‫)'‪userid, 'there‬‬

‫زمانغ په از متد ‪ get‬استفاده مغشود‪ ،‬پلمرد داده شرده بره آن در دیاشرنری بررسرغ‬
‫شده و در صورت وجود آن پلمد‪ ،‬مقدار مورد نظر برگشت داده مغشرود؛ و اگرر پلمرد‬
‫در دیاشنری وجود نداشته باشد‪ ،‬مقدار پمی فرض (در مثال براال ‪ )there‬برگشرت داده‬
‫خواهد شد‪.‬‬

‫)‪>>> greeting(950‬‬
‫'!‪'Hi Bob‬‬

‫)‪>>> greeting(333333‬‬
‫'!‪'Hi there‬‬
‫‪  258‬ترفندهای پایتون‬

‫آخرین روش پماده سازی تابع ‪ greeting‬بسمار تممز و بهمنه بروده و همچنرمن نمراز بره‬
‫پدنویسغ پمتری دارد‪ .‬بنابراین بهتر است در هنگام پار با دیاشنریها از متد ‪ get‬برای‬
‫آنها استفاده شود تا از خطاهای احتمالغ نمز جلوگمری شود‪.‬‬

‫‪ .1-1-7‬نکات کلیدی در هنگام کار با مقادیر پیش فرض در دیکشنریها‬


‫بهتر است تا بزای استفاده از روش قدیمغ برای یرافتن یرک پلمرد در دیاشرنری‪ ،‬از‬
‫متد ‪ get‬استفاده شود‪ .‬بدین ترتمب پدهای برنامه بهمنه تر و ساده تر خواهند شرد‪ .‬گراهغ‬
‫اوقات پمشنهاد مغشود تا از پالس ‪ collections.defaultdict‬در پرایتون اسرتفاده شرود‬
‫تا به برنامه نویسغ بهتر شما پمک نماید‪.‬‬

‫‪ .2-7‬مرتب سازی دیکشنریها‬


‫عناصر موجود در یک دیاشرنری دارای ترتمرب خاصرغ نمسرتند‪ .‬بنرابراین در هنگرام‬
‫پممایی روی یک دیاشنری‪ ،‬همک تضممنغ وجود ندارد په عناصر آن بر اساس ترتمرب‬
‫مشخصغ مورد پردازش قرار گمرند‪.‬‬
‫اما گاهغ اوقات نماز است تا دیاشنری خود را طبق روال خاصغ بر اساس پلمردهای‬
‫موجود در دیاشنری (یا مقرادیر پلمردها) مرترب نمرایمم‪ .‬بررای مثرال فررض پنمرد یرک‬
‫دیاشنری به صورت زیر در اختمار داریم‪.‬‬

‫}‪>>> xs = {'a': 4, 'c': 2, 'b': 3, 'd': 1‬‬

‫برای ایناه بتوانمم پلمد و مقرادیر ایرن دیاشرنری را مرترب پنرمم‪ ،‬مغتروانمم از مترد‬
‫‪ items‬روی این دیاشنری استفاده پرده و سپس از تابع ‪ sorted‬برای مرتب سرازی آن‬
‫استفاده نمایمم‪.‬‬

‫))(‪>>> sorted(xs.items‬‬
‫])‪[('a', 4), ('b', 3), ('c', 2), ('d', 1‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪259 ‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬پلمدها و مقادیر آنها بره صرورت یرک تاپرل نمرایی‬
‫داده شدهاند و سپس تابع ‪ sort‬تمام آنها را در یک لمست قرار داده است‪.‬‬
‫به منظور مقایسهی دو تاپل‪ ،‬تابع ‪ sorted‬اندیس صفرم هر پردام از آنهرا را مقایسره‬
‫مغپند (په در مثال باال همان پلمد دیاشرنری اسرت)‪ .‬در صرورتغ پره هرر دو انردیس‬
‫صفرم برابر بودند‪ ،‬به سراغ اندیس یام مغرود‪ .‬در مثرال براال‪ ،‬تمرام انردیسهای صرفرم‬
‫منحصر به فرد بوده و عملمات مرتب سازی به سادگغ انزام مغشود‪.‬‬
‫گاهغ اوقات ممان است قصد داشته باشرمم ترا یرک دیاشرنری را براسراس مقرادیر‬
‫پلمدهای آنها مرتب نمایمم‪ .‬این عمل را برا اسرتفاده از آرگومران ‪ key‬در ترابع ‪sorted‬‬
‫مغتوانمم انزام دهمم‪ Key .‬در واقع یک تابع پایتون را از ما دریافت مغپند په برا توجره‬
‫به آن تابع‪ ،‬مقادیر یک تاپل را مرتب مغنماید‪ .‬توجره داشرته باشرمد پره اینزرا منظرور از‬
‫‪ ،key‬پلمد موجود در دیاشنریها نمست؛ بلاه پلمدی است په تابع ‪ sorted‬برا اسرتفاده‬
‫از آن عملمات مرتب سازی را انزام مغدهد‪.‬‬
‫فرض پنمد په همان دیاشنری مثال قبل را مغخرواهمم برر اسراس مقرادیر پلمردهای‬
‫موجود در آن مرتب نمایمم‪ .‬بدین منظور از تابعغ به صورت ‪ lambda‬اسرتفاده مرغپنمم‬
‫په برای ایناار از اندیس یام هر تاپل برای مرتب سازی استفاده مغنماید‪.‬‬

‫)]‪>>> sorted(xs.items(), key=lambda x: x[1‬‬


‫])‪[('d', 1), ('c', 2), ('b', 3), ('a', 4‬‬

‫همانطور په مشاهده مغپنمد لمستغ از تاپلها په بر اساس مقادیر پلمدهایشان مرتب‬


‫شدهاند به ما نمایی داده شده است‪.‬‬
‫یاغ دیگر از روشهای موجرود بررای اینارار اسرتفاده از پتابخانرهی ‪ operator‬در‬
‫پایتون است‪ .‬در این پتابخانه بسماری از توابع پلمدی مرسوم در پایتون پماده سازی شرده‬
‫است‪ .‬مانند تابع ‪ operator.itemgetter‬یا ‪ .operator.attrgetter‬در مثال صفحه بعرد‬
‫همان عملمات مرتب سازی را با استفاده از این پتابخانه انزام دادهایم‪.‬‬
‫‪  260‬ترفندهای پایتون‬

‫‪>>> import operator‬‬


‫))‪>>> sorted(xs.items(), key=operator.itemgetter(1‬‬
‫])‪[('d', 1), ('c', 2), ('b', 3), ('a', 4‬‬

‫گاهغ اوقات برای استفاده از برخغ از توابع پمچمده‪ ،‬استفاده از پتابخانهی ‪operator‬‬
‫به خوانایغ بمشتر پد شما پمک مغپند‪ .‬اما برای توابع سادهای ماننرد مثرال گفتره شرده‪،‬‬
‫استفاده از ‪ lambda‬پمشنهاد مغشود‪.‬‬
‫همچنمن یاغ دیگر از مزیتهای استفاده از ‪ lambda‬در این است پره مغتوانمرد بره‬
‫طور پامل خروجغ آن را به صورت دلخواه مدیریت پنمد‪ .‬برای مثال مغتوانمرد مرترب‬
‫سازی یک دیاشنری را با محاسبهی قدر مطلق مقادیر موجود در آن انزام دهمد‪.‬‬

‫))]‪>>> sorted(xs.items(), key=lambda x: abs(x[1‬‬

‫در صورتغ په بخواهمرد خروجرغ شرما بررعاس نمرایی داده شرود (از بزرگترر بره‬
‫پوچاتر)‪ ،‬تنها پافغ اسرت آرگومران ‪ reverse‬را در ترابع ‪ sorted‬برابرر برا ‪ True‬قررار‬
‫دهمد‪.‬‬

‫‪>>> sorted(xs.items(),‬‬
‫‪key=lambda x: x[1],‬‬
‫)‪reverse=True‬‬
‫])‪[('a', 4), ('b', 3), ('c', 2), ('d', 1‬‬

‫همانطور په پمی تر نمز اشاره شد‪ ،‬بهتر است تا با برخغ از توابرع پلمردی در پرایتون‬
‫آشنا شوید‪ .‬این توابع به پد نویسغ شما پمک شایانغ مغپنند و همچنمن برا اسرتفاده از‬
‫آنها مغتوانمد یک نو داده را به نوعغ دیگر نمز تبدیل پنمد‪.‬‬

‫‪ .1-2-7‬نکات کلیدی در هنگام مرتب سازی دیکشنریها‬


‫هنگامغ په از تابع ‪ sorted‬در پایتون استفاده مغپنمم‪ ،‬مغتوانمم مشخص نمایمم پره‬
‫مرتب سازی بر اساس پدام اندیس موجود در تاپل انزام شود‪ .‬برخغ از توابع پر استفاده‬
‫برای ایناار در پتابخانرهی ‪ operator‬نمرز وجرود دارد‪ .‬البتره پمشرنهاد مغشرود ترا بررای‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪261 ‬‬

‫مدیریت بمشتر به منظرور مرترب سرازی عناصرر‪ ،‬از توابرع بره صرورت ‪ lambda‬اسرتفاده‬
‫نمایمد‪.‬‬

‫‪ .3-7‬پیاده سازی عبارت ‪ switch/case‬با استفاده از دیکشنری‬


‫همانطور په مغدانمد در پایتون دستورات ‪ switch/case‬وجود ندارد‪ .‬بنابراین گاهغ‬
‫اوقات ممان است نماز باشد تا عبارات طوالنغ شرطغ بره صرورت ‪ if..elif..else‬نوشرته‬
‫شود‪ .‬ما در این بخی قصد داریم برا روشرغ آشرنا شرویم پره برا اسرتفاده از دیاشرنری‪،‬‬
‫عبارات ‪ switch/case‬را شبمه سازی نمایمم‪.‬‬
‫فرض پنمد په قطعه پدی به صورت زیر در اختمار داریم‪.‬‬

‫‪>>> if cond == 'cond_a':‬‬


‫)(‪handle_a‬‬
‫‪elif cond == 'cond_b':‬‬
‫)(‪handle_b‬‬
‫‪else:‬‬
‫)(‪handle_default‬‬

‫مسلما برای بررسغ ‪ 3‬حالت مختلف‪ ،‬این عبارت به اندازه پافغ مناسب است‪ .‬اما اگر‬
‫تعداد حاالت مورد نماز برای بررسغ بمشتر باشد بایرد چره پرار پررد؟ اگرر بخرواهمم ‪10‬‬
‫حالت مختف را بررسغ پنمم‪ ،‬پد ما بسمار پمچمده و طوالنغ خواهد شد‪.‬‬
‫یاغ از راهها برای رفع این مشال استفاده از دیاشنریها است‪ .‬پایتون دارای توابعغ‬
‫به صورت درجه یک‪ 1‬است؛ یعنغ آنها مغتوانند بره عنروان آرگومران بره توابرع دیگرر‬
‫داده‬ ‫منتقل شده‪ ،‬به عنوان مقدار از توابع دیگر بازگردانده شروند‪ ،‬بره متغمرهرا اختصرا‬
‫شده و حتغ در ساختارهای دادهای ذخمره شوند‪.‬‬
‫برای شفاف سازی بمشتر مثال صفحه بعد را در نظر بگمرید‪ .‬در این قطعه پد یک تابع‬
‫تعریف نمودیم و سپس برای دسترسرغ بره آن در آینرده‪ ،‬آن را در یرک لمسرت ذخمرره‬
‫مغپنمم‪.‬‬

‫‪1 first-class functions‬‬


‫‪  262‬ترفندهای پایتون‬

‫‪>>> def myfunc(a, b):‬‬


‫‪return a + b‬‬

‫]‪>>> funcs = [myfunc‬‬


‫]‪>>> funcs[0‬‬
‫>‪<function myfunc at 0x107012230‬‬

‫برای فراخوانغ این تابع‪ ،‬باید به اندیس لمست مرورد نظرر دسترسرغ پمردا پنرمم؛ و در‬
‫ادامه مقادیری را به عنوان آرگومان به تابع خود انتقال دهمم‪.‬‬

‫)‪>>> funcs[0](2, 3‬‬


‫‪5‬‬

‫حال با باارگمری این ایده قصد داریم تا بالکهرای ‪ if..else‬طروالنغ خرود را بهمنره‬
‫پنمم‪ .‬در ادامه یک دیاشنری تعریف پرده و سرپس بررای پلمردهای آن‪ ،‬تروابعغ را بره‬
‫عنوان مقدار در نظر مغگمریم‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫{ = ‪>>> func_dict‬‬
‫‪'cond_a': handle_a,‬‬
‫‪'cond_b': handle_b‬‬
‫}‬

‫در مثال باال‪ handle_a ،‬و ‪ handle_b‬دو تابع هستند په هر پدام برای یک پلمد در‬
‫نظر گرفته شدهاند‪ .‬حال بزای اسرتفاده از ‪ ،if/else‬مقردار پلمردی را در ایرن دیاشرنری‬
‫جست و جو پرده و سپس تابع مربوط به آن را فراخوانغ مغپنمم تا عملمات مرورد نظرر‬
‫برای حالتغ په قصد داریم بررسغ شود‪ ،‬انزام گمرد‪.‬‬

‫'‪>>> cond = 'cond_a‬‬


‫)(]‪>>> func_dict[cond‬‬

‫در صورتغ په ‪ ،cond‬در دیاشنری ما وجود داشته باشد‪ ،‬تابع مربوط به آن صدا زده‬
‫خواهد شد‪ .‬اما اگر این ‪ cond‬در دیاشنری وجود نداشته باشد‪ ،‬خطای ‪ KeyError‬به مرا‬
‫برگشت داده مغشود‪ .‬این خطای ‪ KeyError‬در واقع همان بالک ‪ else‬ما خواهد بود‪.‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪263 ‬‬

‫برررای رفررع ایررن مشررال از متررد ‪ get‬روی دیاشررنری خررود اسررتفاده مررغپنمم (برررای‬
‫اطالعات بمشتر به بخی ‪ 7.1‬مراجعه پنمد)‪ .‬برا اسرتفاده از ایرن مترد‪ ،‬ترابعغ را بره عنروان‬
‫مقدار پمی فرض (در صورت عدم وجود پلمد در دیاشنری) نمز به عنوان آرگومان بره‬
‫آن پاس مغدهمم‪ .‬در صورتغ په پلمد موجود پمدا نشود‪ ،‬این تابع فراخوانغ مغشود‪.‬‬

‫)()‪>>> func_dict.get(cond, handle_default‬‬

‫قطعه پد باال نمز همانند مثال قبل عمل مغپند‪ .‬تنها با ایرن تفراوت پره ترابعغ نمرز بره‬
‫عنوان مقدار پمی فرض بررای معرادل سرازی برالک ‪ else‬در نظرر گرفتره شرده اسرت و‬
‫همچنمن از خطای ‪ KeyError‬نمز جلوگمری شده است‪.‬‬
‫بمایمد پمغ این حالت را پمچمده تر پرده و مثالغ پامل تر را بررسغ نمرایمم‪ .‬پرس از‬
‫خواندن مثال زیر‪ ،‬دیگر نوشتن هرگونه عبارت ‪ if..elif..else‬به صورت دیاشنری بررای‬
‫شما ساده خواهد شد‪.‬‬
‫ابتدا تابعغ را به صورت ‪ if..elif..else‬پماده سازی مغپنمم‪ .‬ایرن ترابع مقرداری را بره‬
‫عنوان آپاد دریافت مغنماید په مشخص پنندهی نو عملمات مورد نظر است (جمرع‪،‬‬
‫تفریق‪ ،‬ضرب و تقسمم)؛ و دو مقدار دریافتغ دیگر با توجه به عملمات مورد نظر جمع یرا‬
‫ضرب یا … خواهند شد‪.‬‬

‫‪>>> def dispatch_if(operator, x, y):‬‬


‫‪if operator == 'add':‬‬
‫‪return x + y‬‬
‫‪elif operator == 'sub':‬‬
‫‪return x - y‬‬
‫‪elif operator == 'mul':‬‬
‫‪return x * y‬‬
‫‪elif operator == 'div':‬‬
‫‪return x / y‬‬
‫‪  264‬ترفندهای پایتون‬

‫حال از تابع ‪ dispatch_if‬په در باال نوشتمم مغتوانمم به صورت زیر استفاده پنمم‪.‬‬

‫)‪>>> dispatch_if('mul', 2, 8‬‬


‫‪16‬‬
‫)‪>>> dispatch_if('unknown', 2, 8‬‬
‫‪None‬‬

‫توجه داشته باشمد زمرانغ پره از ‪ unknown‬بره عنروان آپارد بررای ترابع مرورد نظرر‬
‫استفاده مغپنمم‪ ،‬مقدار ‪ None‬به ما نمرایی داده مغشرود‪ .‬چررا پره پرایتون بره صرورت‬
‫ضمنغ در انتهای تمام توابع عبارت ‪ return None‬را مغنویسد‪.‬‬
‫حال قصد داریم تابع ‪ dispatch_if‬را تغممر داده تا با استفاده از دیاشنری‪ ،‬شررایط و‬
‫حاالت مختلف را بررسغ نمایمم‪ .‬این تابع را ‪ dispatch_dict‬نامگذاری مغپنمم‪.‬‬

‫‪>>> def dispatch_dict(operator, x, y):‬‬


‫{ ‪return‬‬
‫‪'add': lambda: x + y,‬‬
‫‪'sub': lambda: x - y,‬‬
‫‪'mul': lambda: x * y,‬‬
‫‪'div': lambda: x / y,‬‬
‫)()‪}.get(operator, lambda: None‬‬

‫این تابع همانند تابع قبل عمل پرده و دقمقا همان خروجغ را به ما برمغگرداند‪.‬‬

‫)‪>>> dispatch_dict('mul', 2, 8‬‬


‫‪16‬‬
‫)‪>>> dispatch_dict('unknown', 2, 8‬‬
‫‪None‬‬

‫راههای بسماری وجود دارد تا بتوان تابع ‪ dispatch_dict‬را باز هم بهبود بخشمد‪.‬‬
‫هر زمان په ما این تابع را فراخوانغ مغپنمم‪ ،‬یک دیاشنری به همرراه تعردادی ترابع‬
‫‪ lambda‬ایزاد مغشود په از لحاظ عملارد بسمار بد اسرت‪ .‬بررای بهبرود عملاررد ایرن‬
‫تابع‪ ،‬بهتر است تا دیاشنری مورد نظر تنها یابار به صرورت ابرت (‪ )constant‬تعریرف‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪265 ‬‬

‫شود و زمانغ په تابع فراخوانغ مغشود‪ ،‬به آن دیاشنری اشاره گردد‪ .‬بدین ترتمب دیگر‬
‫نمازی به ساخت یک دیاشنری پس از هربار فراخوانغ تابع نمغباشد‪.‬‬
‫همچنمن برای استفاده از عملگرهای سادهای مانند جمع یرا ضررب‪ ،‬بهترر اسرت ترا از‬
‫توابع داخلغ پایتون و پتابخانهی ‪( operator‬بزرای ترابع ‪ )lambda‬اسرتفاده شرود‪ .‬ایرن‬
‫پتابخانه اپثر عملگرهای مورد نماز را برای ما فراهم پرده است‪ .‬ماننرد ‪،operator.add‬‬
‫‪ operator.mul‬و … ‪ .‬اما در مثال باال‪ ،‬دلمل استفاده از ‪ lambda‬تنها نشان دادن قابلمت‬
‫پماده سازی یک دیاشنری برای حاالت مختلف و انزام یاسری عملمات بوده است‪.‬‬
‫بدین ترتمرب مغتروانمم بسرماری از قطعره پردهای خرود را پره برا عبرارات طروالنغ‬
‫‪ if..elif..else‬نوشته شدهاند‪ ،‬بدین صورت بازنویسغ نمایمم‪ .‬البته توجه داشته باشمد په از‬
‫این تانمک در همهی شرایط استفاده نانمد‪ .‬عبارات پوتاه ‪ if..else‬بهتر است تا به همان‬
‫صورت نوشته شوند‪.‬‬

‫‪ .1-3-7‬نکات کلیدی در هنگـام معـادل سـازی عبـارات ‪ switch/case‬در‬


‫پایتون‬
‫در پایتون عبارات ‪ switch/case‬وجود ندارد؛ اما با استفاده از دیاشرنریها مغتروان‬
‫عبارات طوالنغ ‪ if..elif..else‬را بهمنه نموده و پد خود را ساده تر نمایمم‪.‬‬

‫‪ .4-7‬نکاتی که هنگام کار با دیکشنریها باید به آن ها توجه کنید‬


‫گاهغ اوقات هنگام برنامه نویسغ با قطعره پردهایغ روبررو مغشرویم پره دیرد مرا را‬
‫نسبت به آن زبان باز تر مغپند و ممان است حتغ یک خط پد باعرث شرود پره یرک‬
‫زبان برنامه نویسغ را خملغ بمشتر از پمی درک پنمم‪.‬‬
‫قطعه پدی په در این بخی مغخواهمم در مورد آن صحبت پنمم‪ ،‬یرک دیاشرنری‬
‫بوده په ممان است در نگاه اول بسمار ساده به نظر بمایرد؛ امرا برا تروجهغ بمشرتر بره آن‪،‬‬
‫‪  266‬ترفندهای پایتون‬

‫باعث مغشود تا نحوهی پارپرد مفسر‪ 1‬پایتون را بهتر درک نمرایمم‪ .‬بره قطعره پرد زیرر‬
‫توجه پنمد‪.‬‬

‫}'‪>>> {True: 'yes', 1: 'no', 1.0: 'maybe‬‬

‫ابتدا نگاهغ دقمق تر به پد باال بمندازید‪ .‬به نظرتان با نوشتن پد باال‪ ،‬دیاشنری مرورد‬
‫نظر به هممن صورت ساخته خواهد شد؟‬
‫مفسر پایتون قطعه پد باال را به صرورت زیرر تفسرمر پررده و خروجرغ زیرر را بره مرا‬
‫نمایی مغدهد‪.‬‬

‫}'‪>>> {True: 'yes', 1: 'no', 1.0: 'maybe‬‬


‫}'‪{True: 'maybe‬‬

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

‫>>>‬ ‫)(‪xs = dict‬‬


‫>>>‬ ‫'‪xs[True] = 'yes‬‬
‫>>>‬ ‫'‪xs[1] = 'no‬‬
‫>>>‬ ‫'‪xs[1.0] = 'maybe‬‬

‫دادهایم را برابرر‬ ‫در واقع مفسر پایتون تمام پلمدهایغ په به دیاشنری خود اختصا‬
‫با یادیگر مغداند‪.‬‬

‫‪1 interpreter‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪267 ‬‬

‫‪>>> True == 1 == 1.0‬‬


‫‪True‬‬

‫طبمعتا عدد ‪ 1‬با ‪ 1.0‬برابر است‪ .‬اما چرا پایتون ‪ True‬را نمز برابر با ‪ 1‬مغدانرد؟ پرس از‬
‫بررسغ داپمومنت زبان پایتون‪ ،‬متوجه خواهمم شد په ‪ bool‬در واقع زیر پالسرغ از ‪int‬‬
‫است‪ .‬طبق آنچه په در داپمومنت رسمغ پایتون آمده است‪:‬‬

‫نو ‪ bool‬در واقع زیرمزموعه ای از نو ‪ int‬است و مقادیر ‪ bool‬همانند ‪ 0‬و ‪ 1‬عمل‬


‫مغپنند‪.‬‬

‫این بدان معناست په شما حتغ مغتوانمد از ‪bool‬ها به عنوان اندیس یرک لمسرت یرا‬
‫تاپل نمز برای فراخوانغ عنصری از آنها نمز استفاده نمایمد‪.‬‬

‫]‪>>> ['no', 'yes'][True‬‬


‫'‪'yes‬‬

‫اما به هر حال ایناار به همک وجره پمشرنهاد نمغشرود‪ .‬چررا پره خوانرایغ پرد شرما را‬
‫سخت مغپند‪.‬‬
‫حال از آنزا په پایتون مقدار ‪ True‬را برا ‪ 1‬و ‪ 1.0‬برابرر مغدانرد‪ ،‬در هنگرام سراخت‬
‫دیاشنری‪ ،‬هر بار مقدار این پلمد با مقدار جدید جایگزین مغشود‪ .‬به هممن دلمل اسرت‬
‫په در انتها‪ ،‬تنها یک پلمد با نام ‪ True‬وجود داشته و مقدار آن آخررین مقرداری اسرت‬
‫په به ‪ 1.0‬داده شده است؛ یعنغ ‪. maybe‬‬

‫}'‪>>> {True: 'yes', 1: 'no', 1.0: 'maybe‬‬


‫}'‪{True: 'maybe‬‬

‫حال سؤالغ په پمی مغآید این است په چرا تنها مقردار پلمرد تغممرر پررده و خرود‬
‫پلمد بروز نمغشود و برابر با ‪ 1.0‬نمغباشد؟‬
‫دلمل این امر آن است په در واقع مفسر پایتون‪ ،‬خود پلمرد را همچگراه برروز رسرانغ‬
‫نمغپند‪.‬‬
‫‪  268‬ترفندهای پایتون‬

‫}'‪>>> ys = {1.0: 'no‬‬


‫'‪>>> ys[True] = 'yes‬‬
‫‪>>> ys‬‬
‫}'‪{1.0: 'yes‬‬

‫همانطور په در مثال باال مشراهده مغپنمرد‪ ،‬پلمرد ‪ True‬جرایگزین پلمرد ‪ 1.0‬نشرده‬


‫داده شرده‬ ‫است‪ .‬اما مقدار پلمد ‪ 1.0‬بروز رسانغ شده و رشتهای جدید به آن اختصرا‬
‫است‪.‬‬
‫دیاشنریها در پایتون بر اساس یک سراختار دادهی جردول هری‪ 1‬سراخته شردهاند‪.‬‬
‫یک جدول هی‪ ،‬پلمدهای موجود را در باپتهای‪ 2‬مختلف بر اساس مقادیر هری هرر‬
‫پلمد ذخمره مغپند‪ .‬مقدار هی در واقع به عنوان مقداری عددی با طول ابرت از پلمرد‬
‫گرفته مغشود‪ .‬این عمل باعث جست و جوی سریعتر در یک دیاشنری مغشود‪ .‬جست‬
‫و جوی مقدار هی عددیِ یک پلمد‪ ،‬بسمار سریعتر از مقایسهی یرک شرغء از پلمرد برا‬
‫تمام پلمدهای موجود در دیاشنری است‪ .‬اما نحوهی محاسبهی هی به انردازهی پرافغ‬
‫خوب نمست و گاهغ دو پلمد په با یادیگر متفاوت هستند‪ ،‬ممان اسرت مقرادیر هری‬
‫یاسانغ داشته و در یک باپت قرار گمرند‪.‬‬
‫دو پلمدی په دارای مقدار هی یاسان هستند‪ ،‬تصادم هیها‪ 3‬ناممده مغشوند‪ .‬این‬
‫بوده په در این حالت الگوریتمهای جدول هی به منظور یافتن یا قرار‬ ‫یک مورد خا‬
‫دادن عناصر‪ ،‬باید مدیریت شوند‪ .‬بر این اساس‪ ،‬ممان است نتایج رمرر قابرل انتظراری از‬
‫دیاشنریهای خود دریافت پنمم‪ .‬برای شفاف سازی بمشتر‪ ،‬در ادامه پالسغ بره منظرور‬
‫آشنایغ بمشتر با این مفهوم تعریف نمودیم‪.‬‬

‫‪1 hash table data structure‬‬


‫‪2 buckets‬‬
‫‪3 hash collision‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪269 ‬‬

‫‪class AlwaysEquals:‬‬
‫‪def __eq__(self, other):‬‬
‫‪return True‬‬

‫‪def __hash__(self):‬‬
‫)‪return id(self‬‬

‫متد __‪ __eq‬در این پالس هممشه مقدار ‪ True‬را برای مرا برمغگردانرد‪ .‬یعنرغ در‬
‫صورت مقایسهی این پالس با هر شغء دیگری‪ ،‬مقدار ‪ True‬به ما نمایی داده مغشود‪.‬‬

‫)(‪>>> AlwaysEquals() == AlwaysEquals‬‬


‫‪True‬‬
‫‪>>> AlwaysEquals() == 42‬‬
‫‪True‬‬
‫'?‪>>> AlwaysEquals() == 'waaat‬‬
‫‪True‬‬

‫همچنمن هر نمونه از پالس ‪ AlwaysEquals‬یک هی منحصر به فرد را با استفاده از‬


‫تابع ‪ id‬به ما برمغگرداند‪.‬‬

‫‪>>> objects = [AlwaysEquals(),‬‬


‫‪AlwaysEquals(),‬‬
‫])(‪AlwaysEquals‬‬
‫]‪>>> [hash(obj) for obj in objects‬‬
‫]‪[4574298968, 4574287912, 4574287072‬‬

‫در مفسر پایتون‪ ،‬تابع ‪ id‬آدرس شرغء موجرود در حافظره را بره نمرایی مغدهرد پره‬
‫هممشه منحصر به فرد است‪.‬‬
‫با استفاده از این پالس مغتوانمم اشمایغ ایزاد نمایمم په وانمود مغپنند با هر شرغء‬
‫دیگری برابر بوده اما مقدار هی متفاوتغ دارند‪ .‬حرال ایرن پرالس بره مرا ایرن اجرازه را‬
‫مغدهد تا ببمنمم پره آیرا پلمردهای موجرود در یرک دیاشرنری‪ ،‬تنهرا در صرورت برابرر‬
‫بودنشان با یادیگر روی یادیگر نوشته مغشوند (‪ )overwrite‬یا خمر‪.‬‬
‫‪  270‬ترفندهای پایتون‬

‫همانطور په در مثال زیر مشاهده مغپنمد‪ ،‬دیاشنری این دو پلمد را با یادیگر برابر‬
‫نمغداند‪.‬‬

‫}'‪>>> {AlwaysEquals(): 'yes', AlwaysEquals(): 'no‬‬


‫‪{ <AlwaysEquals object at 0x110a3c588>: 'yes',‬‬
‫} '‪<AlwaysEquals object at 0x110a3cf98>: 'no‬‬

‫حال قصد داریم تا مترد __‪ __hash‬را تغممرر دهرمم و هممشره یرک مقردار را از آن‬
‫برگردانمم تا ببمنمم آیا بدین صورت مغشود یک پلمد را روی پلمدی دیگرر نوشرت یرا‬
‫خمر‪.‬‬

‫‪class SameHash:‬‬
‫‪def __hash__(self):‬‬
‫‪return 1‬‬

‫در ادامه مشاهده مغپنمم په نمونههای پالس ‪ SameHash‬با یادیگر برابرر نمسرتند‬
‫اما هر دوی آنها یک مقدار را به عنوان مقدار هی به ما بر مغگردانند‪.‬‬

‫)(‪>>> a = SameHash‬‬
‫)(‪>>> b = SameHash‬‬
‫‪>>> a == b‬‬
‫‪False‬‬
‫)‪>>> hash(a), hash(b‬‬
‫)‪(1, 1‬‬

‫حال اگر از نمونههای این پالس به عنوان پلمدهای یک دیاشرنری اسرتفاده نمرایمم‬
‫چه اتفاقغ رخ خواهد داد؟‬

‫}'‪>>> {a: 'a', b: 'b‬‬


‫‪{ <SameHash instance at 0x7f7159020cb0>: 'a',‬‬
‫} '‪<SameHash instance at 0x7f7159020cf8>: 'b‬‬

‫همانطور په مشاهده مغ پنمد‪ ،‬در مثال باال نمز پلمدها روی یادیگر نوشته نشدهاند‪.‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪271 ‬‬

‫دیاشنریها برابری را بررسغ پرده و مقادیر هیها را با یادیگر مقایسه مغپنند ترا‬
‫تشخمص دهند په دو پلمد با یادیگر برابر هستند یا خمر‪ .‬بمایمد تا بره جمرع بنردی آنچره‬
‫په تا بدین جا فهممدهایم بپردازیم‪:‬‬
‫برره طررور پلررغ دیاشررنری }”‪ {True: ‘yes’, 1: “no”, 1.0: “maybe‬تبرردیل برره‬
‫دیاشنری }”‪ {True: “maybe‬خواهد شد‪ .‬چراپه پلمردهای ‪ 1 ،True‬و ‪ 1.0‬هرر سره برا‬
‫یادیگر برابر بوده و دارای مقدار هی یاسانغ هستند‪.‬‬

‫‪>>> True == 1 == 1.0‬‬


‫‪True‬‬
‫))‪>>> (hash(True), hash(1), hash(1.0‬‬
‫)‪(1, 1, 1‬‬

‫حال دیگر مشاهدهی خروجغ زیر به هنگام ساخت این دیاشنری برای شما رمر قابل‬
‫انتظار نخواهد بود‪.‬‬

‫}'‪>>> {True: 'yes', 1: 'no', 1.0: 'maybe‬‬


‫}'‪{True: 'maybe‬‬

‫اگر درک موضوعات مطرح شده در این بخی برای شما پمغ دشوار اسرت‪ ،‬سرعغ‬
‫پنمد نمونه پدهای موجود را خودتان در مفسر پایتون بنویسمد تا بتوانمرد ایرن مفراهمم را‬
‫بهتر فرا گمرید‪ .‬بدین صورت دانی شما از نحوهی پرارپرد زبران پرایتون بمشرتر خواهرد‬
‫شد‪.‬‬

‫‪ .1-4-7‬نکات کلیدی که در هنگام کار با دیکشنریها باید به آنها توجه‬


‫نمود‬
‫پلمدهای موجود در دیاشنری در صورتغ په متد __‪ __eq‬در آنها برابر باشند و‬
‫همچنمن مقادیر هی آنها یاسان باشند‪ ،‬روی یادیگر نوشته شده و با هم برابر در نظرر‬
‫گرفته مغشوند‪ .‬به طور پلغ تصادم پلمدهای دیاشنری ممان است باعث ایزراد نترایج‬
‫رمر منتظرهای شود‪.‬‬
‫‪  272‬ترفندهای پایتون‬

‫‪ .5-7‬ادغام دیکشنریها با یکدیگر‬


‫آیا تا بحال پمی آمده پره بخواهمرد یرک پماربنردی یرا تنظممراتغ بررای برنامرههای‬
‫پایتونغ خود ایزاد پنمد؟ یاغ از موارد رایج برای ایزاد چنمن پماربندیهایغ‪ ،‬اسرتفاده‬
‫از یک ساختار دادهی مناسب بوده په دارای تنظممات و مقرادیری پرمی فررض اسرت و‬
‫پاربر مغتواند در ادامه تنظممات شخصغ خود را بزای آنها اعمال پند‪.‬‬
‫در اپثر برنامههای پایتونغ‪ ،‬از دیاشنری به عنوان ساختار دادهی مربوط به پماربندی‬
‫برنامهها اسرتفاده مغشرود‪ .‬پماربنردیها در دیاشرنری بره صرورت یرک پلمرد و مقردار‬
‫(‪ )key/value‬تعریف شده و پاربر مغتواند مقدار هر پلمد را در آینده با مقادیر مناسب‬
‫خود (تنظممات شخصغ) جایگزین نماید‪ .‬بنابراین نمراز اسرت روشرغ را فراگمرریم ترا برا‬
‫استفاده از آن به ترپمب یا ادرام تنظممات پمی فرض در دیاشنریها با مقرادیر دلخرواه‬
‫بپردازیم‪ .‬همچنمن گاهغ اوقات ممان است بخواهمد دو یا چند دیاشنری را با هم ادرام‬
‫نمایمد و تمام آنها را در یک دیاشنری داشته باشمد‪.‬‬
‫در این بخی برای انزام این موارد‪ ،‬با راههای مختلفغ آشنا خواهمم شد‪.‬‬
‫برای مثال فرض پنمد دو دیاشنری به صورت زیر در اختمار داریم‪.‬‬

‫}‪>>> xs = {'a': 1, 'b': 2‬‬


‫}‪>>> ys = {'b': 3, 'c': 4‬‬

‫حال قصد داریم تا یک دیاشنری برا نرام ‪ zs‬بسرازیم پره تمرام پلمردها و مقرادیر دو‬
‫دیاشنری ‪ xs‬و ‪ ys‬درون آن وجود داشته باشد‪ .‬اگر به مثال براال بمشرتر توجره پنمرد‪ ،‬پرغ‬
‫خواهمد برد په رشتهی ‪ b‬به عنوان پلمد در هر دو دیاشرنری وجرود دارد‪ .‬بنرابراین نمراز‬
‫است تا این مغایرت‪ 1‬در دو دیاشنری را نمز به نوعغ رفع نمایمم‪.‬‬
‫یاغ از روشهای مرسوم برای ادرام چنمن دیاشنریهایغ در پایتون‪ ،‬استفاده از متد‬
‫‪ update‬موجود در دیاشنری است‪.‬‬

‫‪1 conflict‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪273 ‬‬

‫}{ = ‪>>> zs‬‬


‫)‪>>> zs.update(xs‬‬
‫)‪>>> zs.update(ys‬‬

‫در واقع متد ‪ update‬تمام عناصر موجود (پلمدها و مقادیرشان) در دیاشنری سمت‬
‫راست را در دیاشنری سمت چپ مغنویسرد و در صرورتغ پره پلمردی در دیاشرنری‬
‫سمت چپ وجود داشته باشد‪ ،‬با مقدار جدید از پلمد سمت راست جایگزین مغشود‪.‬‬

‫‪def update(dict1, dict2):‬‬


‫‪for key, value in dict2.items():‬‬
‫‪dict1[key] = value‬‬

‫در نهایت دیاشنری حاصل ‪ zs‬شامل عناصر زیر خواهد بود‪.‬‬

‫‪>>> zs‬‬
‫}‪>>> {'c': 4, 'a': 1, 'b': 3‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬از آنزا په آخررین مترد ‪ update‬روی دیاشرنری ‪ys‬‬
‫انزام شده است‪ ،‬پلمد ‪ b‬با مقدار موجود در ‪ ys‬برای دیاشنری ‪ zs‬قرار داده شده اسرت‪.‬‬
‫شما مغتوانمد در ادامه نمز باز هم از این متد استفاده پرده تا تعداد دیاشنریهای بمشتری‬
‫را درون ‪ zs‬ادرام نمایمد‪.‬‬
‫یاغ دیگر از ترفندهای موجود برای ایزاد یرک دیاشرنری برا ادررام دو دیاشرنری‬
‫دیگر‪ ،‬استفاده از تابع ‪ dict‬به همراه عملگر ** در پایتون است‪.‬‬

‫)‪>>> zs = dict(xs, **ys‬‬


‫‪>>> zs‬‬
‫}‪{'a': 1, 'c': 4, 'b': 3‬‬

‫البته توجه داشته باشمد په این روش برای ادرام تنها دو دیاشنری باار برده مغشرود‬
‫نمود‪1.‬‬ ‫و نمغتوان در یک خط بمی از دو دیاشنری را با یادیگر ادرام‬

‫‪ 1‬برای ادرام بمی از دو دیاشنری باید دوباره این دستور را برای دیاشنریهای جدید بنویسمد‪.‬‬
‫‪  274‬ترفندهای پایتون‬

‫همچنمن در نسخهی پایتون ‪ 3.5‬به بعد‪ ،‬شما مغتوانمد از عملگر ** به نوعغ دیگر نمز‬
‫برای ادرام دیاشنریها پمک بگمرید‪ .‬این روش یاغ از روشهای بهمنره بررای اینارار‬
‫است و قدرت برنامه نویسغ شما را افزایی مغدهد‪ .‬از این عملگرر مغتروان بره صرورت‬
‫زیر برای ادرام تعداد دلخواهغ از دیاشنریها استفاده نمود‪.‬‬

‫}‪>>> zs = {**xs, **ys‬‬

‫عملارد این روش دقمقا برابر با روشهای قبل است‪ .‬در قطعه پد باال نمرز دیاشرنری‬
‫سمت راست اولویت را بدسرت گرفتره و در صرورتغ پره پلمردی تارراری در ایرن دو‬
‫دیاشنری وجود داشته باشد‪ ،‬مقدار این پلمرد برابرر برا مقردار موجرود در دیاشرنری ‪ys‬‬
‫خواهد بود‪ .‬خروجغ حاصل از دستور باال در دیاشنری ‪ zs‬قرار داشته په به صورت زیر‬
‫است‪.‬‬

‫‪>>> zs‬‬
‫}‪>>> {'c': 4, 'a': 1, 'b': 3‬‬

‫استفاده از این سمنتاس جدید‪ ،‬پار را برای ما بسمار آسان پرده است‪ .‬برا اسرتفاده از‬
‫این روش دیگر نمازی به نوشتن چندین متد ‪ update‬برای ادررام برمی از دو دیاشرنری‬
‫نمغباشد‪ .‬بنابراین عملگر ** در پایتون ‪ 3‬بسمار پاربردی بوده و پار را برای ما ساده ترر‬
‫مغپند‪.‬‬

‫‪ .1-5-7‬نکات کلیدی در هنگام ادغام دیکشنریها‬


‫در نسخهی پایتون ‪ 3.5‬به بعد‪ ،‬شما مغتوانمد با پمک عملگر ** تعرداد دلخرواهغ از‬
‫دیاشنریها را در یک دیاشنری ادرام نمایمد‪ .‬توجه داشته باشمد په در صورت وجرود‬
‫پلمدی تاراری در چندین دیاشنری‪ ،‬اولویت با سمت راست ترین دیاشنری است‪.‬‬
‫اما اگر قصد داشته باشمد تا برنامهی شما با نسخههای قبل از پایتون ‪ 3.5‬نمز سازگاری‬
‫داشته باشد‪ ،‬بهتر است تا از متد ‪ update‬برای ادرام دیاشنریهای خود استفاده پنمد‪.‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪275 ‬‬

‫‪ .6-7‬چاپ و نمایش بهتر دیکشنریها‬


‫مطمئنررا شررما هررم در هنگررام پررد نویسررغ از ‪ print‬برررای اشرراال زدایررغ و دیبرراگ‬
‫برنامههایتان استفاده پردهاید‪ .‬یا شاید ممان است از ‪ log‬برای ایناار استفاده نمایمد‪ .‬امرا‬
‫برخغ از دادهها هنگامغ په با ‪ print‬نمایی داده مغشوند‪ ،‬خواندن آنهرا پمرغ دشروار‬
‫است‪ .‬یاغ از انوا این دادهها دیاشنری است‪.‬‬
‫در مثال زیر‪ ،‬یک دیاشنری تعریف شده اسرت‪ .‬در هنگرام نمرایی ایرن دیاشرنری‪،‬‬
‫ترتمب پلمدها لحاظ نشده و همچنمن همک گونه تو رفتگغ‪ 1‬نمز در هنگرام نمرایی نترایج‬
‫وجود ندارد‪.‬‬

‫}‪>>> mapping = {'a': 23, 'b': 42, 'c': 0xc0ffee‬‬


‫)‪>>> str(mapping‬‬
‫}‪{'b': 42, 'c': 12648430, 'a': 23‬‬

‫خوشبختانه روشهای دیگری نمرز بزرای اسرتفاده از ترابع ‪ str‬وجرود دارد پره بررای‬
‫نمایی زیباتر یک دیاشنری باار برده مغشود‪ .‬یاغ از این روشها‪ ،‬اسرتفاده از مراژول‬
‫‪ json‬در پایتون است‪ .‬با پمک ‪ json.dumps‬مغتوان دیاشنریها را به صورتغ زیبراتر‬
‫نمایی داد‪.‬‬

‫‪>>> import json‬‬


‫)‪>>> json.dumps(mapping, indent=4, sort_keys=True‬‬
‫{‬
‫‪"a": 23,‬‬
‫‪"b": 42,‬‬
‫‪"c": 12648430‬‬
‫}‬

‫خروجغ باال بسمار زیبا تر از حالت قبرل برا ترو رفتگغهرای مناسرب بروده و همچنرمن‬
‫پلمدهای دیاشنری نمز در آن به ترتمب مرتب شدهاند‪.‬‬

‫‪1 indention‬‬
‫‪  276‬ترفندهای پایتون‬

‫درحالغ په دیاشنریها بدین صورت بسمار زیبا و قابل خوانا نمایی داده مغشروند‪،‬‬
‫اما این روشغ پامل و بدون نقص نمست‪ .‬نمایی دیاشنریها با استفاده از مراژول ‪،json‬‬
‫تنها برای دیاشنریهایغ مو ر است په مغتوانند با اسرتفاده از آن سرریال‪ 1‬شروند‪ .‬انروا‬
‫دادههای قابل پشتمبانغ در آن عبارتند از‪:‬‬

‫‪dict, list, tuple, str, int, float, bool, None‬‬

‫بنابراین هنگامغ په بخواهمد یک دیاشنری را چاپ پنمد پره شرامل یارغ از انروا‬
‫داده بزز موارد باال باشد(برای مثال یک تابع)‪ ،‬با خطا مواجه خواهمد شد‪.‬‬

‫)}'‪>>> json.dumps({all: 'yup‬‬


‫"‪TypeError: "keys must be a string‬‬

‫همچنمن در صورتغ په از نو دادهی ‪ set‬نمز استفاده نمایمم‪ ،‬به خطا برمغخوریم‪.‬‬

‫}‪>>> mapping['d'] = {1, 2, 3‬‬


‫)‪>>> json.dumps(mapping‬‬
‫"‪TypeError: "set([1, 2, 3]) is not JSON serializable‬‬

‫حال بهتر است روشغ دیگر را مورد بررسغ قرار دهمم ترا دیگرر برا چنرمن مشراالتغ‬
‫مواجه نشویم‪ .‬یاغ از روشهای قدیمغ و مرسوم برای نمرایی زیبرای اشرما در پرایتون‪،‬‬
‫استفاده از ماژول ‪ pprint‬است‪ .‬به مثال زیر توجه پنمد‪.‬‬

‫‪>>> import pprint‬‬


‫)‪>>> pprint.pprint(mapping‬‬
‫‪{'a': 23, 'b': 42, 'c': 12648430, 'd': set([1, 2,‬‬
‫})]‪3‬‬

‫همانطور پره مشراهده مغپنمرد‪ ،‬ایرن مراژول توانرایغ نمرایی دادهی ‪ set‬را داشرته و‬
‫همچنمن پلمدها را نمز به ترتمب نمرایی مغدهرد‪ .‬در مقایسره برا روش قردیمغ و سرنتغ‪،‬‬
‫خروجغ حاصل از این روش زیباتر بوده و قابلمت خوانایغ باالتری دارد‪.‬‬

‫‪1 serialized‬‬
‫فصل هفتم‪ :‬ترفندهایی برای کار با دیکشنریها ‪277 ‬‬

‫اگرچرره در مقایسرره بررا روش ‪ ،json.dumps‬بررا اسررتفاده از ایررن روش نمغترروان‬


‫تورفتگغهایغ برای خروجغ یرک دیاشرنری در نظرر گرفرت‪ .‬بره طرور پلرغ‪ ،‬پمشرنهاد‬
‫مغشود اگر در دیاشنری تنها دادههایغ وجرود دارد پره قابرل سرریال شردن هسرتند‪ ،‬از‬
‫‪ json.dumps‬برای نمایی زیبا تر آنها استفاده پنمد‪.‬‬

‫‪ .1-6-7‬نکات کلیدی در هنگام نمایش دیکشنریها‬


‫برره صررورت پررمی فرررض‪ ،‬خروجغهررای حاصررل از نمررایی دیاشررنریها در پررایتون‬
‫خوانایغ باالیغ ندارند‪ .‬مغتوان برای ایناه نمایی زیباتری از دیاشنریهای خود داشرته‬
‫باشمم‪ ،‬از ماژولهایغ مانند ‪ pprint‬یا ‪ json‬در پایتون استفاده نمرایمم‪ .‬البتره توجره داشرته‬
‫باشمد په در هنگام استفاده از ماژول ‪ ،json‬تنها از دادههایغ برا قابلمرت سرریال شردن در‬
‫دیاشنری خود استفاده پنمد‪.‬‬
‫فصل هشتم‪ :‬تانمکهایغ برای بهره وری بمشتر از زبان پایتون‬

‫فصل هشتم‬

‫تکنیکهایی برای بهره وری بیشتر از‬


‫زبان پایتون‬
‫‪ .1-8‬آشنایی بیشتر با ماژولها در پایتون‬
‫در زبان برنامه نویسغ پایتون‪ ،‬شما به راحترغ مغتوانمرد برا نوشرتن یرک خرط پرد در‬
‫ابتدای برنامه‪ ،‬از ماژولهای مختلفغ در هنگام پد نویسغ استفاده پنمد‪ .‬این پار در سایر‬
‫زبان های برنامه نویسغ ممان است پمغ دشوار تر باشد‪ .‬در بسماری از زبانهرای برنامره‬
‫نویسغ اسرتفاده از یرک پارمج‪ ،‬بردون خوانردن داپمومنتهرای آن و آشرنایغ پامرل برا‬
‫نحوهی پارپرد آنها‪ ،‬بسمار سخت است‪ .‬در پایتون‪ ،‬شما مغتوانمد با ورود به مفسر خط‬
‫فرمان آن (‪ )REPL‬و استفاده از دسرتورات خاصرغ‪ ،‬اپثرر قابلمتهرا و ویژگغهرای یرک‬
‫ماژول را دریابمد و با آن آشنا شوید‪.‬‬
‫در این بخی قصد داریم تا با نحوهی پار برا ماژولهرا در خرط فرمران پرایتون آشرنا‬
‫شویم تا بتوانمم به سادگغ از آنها در برنامههای خرود اسرتفاده پنرمم‪ .‬تنهرا پرافغ اسرت‬
‫دستور ‪ python‬را در خط فرمان خود اجرا پرده و وارد مفسر پایتون شوید‪ .‬این قابلمرت‬
‫به شما این اماان را مغدهد تا بدون اجرای پامل برنامههای خود‪ ،‬بره بررسرغ و اشراال‬
‫زدایغ بخیهایغ از پدتان بپردازید و به سادگغ قطعه پردهایغ پره احسراس مغپنمرد‬
‫مشال دارند را دیباگ پنمد‪.‬‬
‫فرض پنمد در برنامهای از ماژول ‪ datetime‬استفاده پردهایم‪ .‬حرال قصرد داریرم ترا‬
‫بفهممم در این ماژول چه پالسها و توابعغ وجود دارد و چگونه مغتوانمم بسته بره نمراز‬
‫از توابع و پالسهای موجود در این ماژول استفاده پنمم‪.‬‬
‫یاغ از راههای آن جست و جو در اینترنت و خواندن مستندات پامل ایرن پتابخانره‬
‫است‪ .‬اما راه سادهتر‪ ،‬استفاده از دستور ‪ dir‬در پایتون است په به ما این قابلمت را مغدهد‬
‫په به تمام اطالعات مورد نماز از یک ماژول دسترسغ داشته باشمم‪.‬‬
‫‪  282‬ترفندهای پایتون‬

‫‪>>> import datetime‬‬


‫)‪>>> dir(datetime‬‬
‫‪['MAXYEAR', 'MINYEAR', '__builtins__', '__cached__',‬‬
‫‪'__doc__', '__file__', '__loader__', '__name__',‬‬
‫‪'__package__', '__spec__', '_divide_and_round',‬‬
‫‪'date', 'datetime', 'datetime_CAPI', 'time',‬‬
‫]'‪'timedelta', 'timezone', 'tzinfo‬‬

‫در مثال باال‪ ،‬ابتدا پتابخانرهی ‪ datetime‬را ایمپرورت پررده و سرپس برا اسرتفاده از‬
‫دستور ‪ ،dir‬جزیمات مربروط بره آن را مشراهده نمرودیم‪ .‬فراخروانغ ترابع ‪ dir‬روی یرک‬
‫ماژول باعث مغشود تا لمستغ از تمرام ویژگغهرای موجرود در یرک پتابخانره را بره مرا‬
‫نمایی دهد‪.‬‬
‫از آنزا په در پایتون همه چمز به عنوان یک شغء (‪ )object‬در نظر گرفته مغشرود‪،‬‬
‫با استفاده از این تابع مغتوانمم مشخصات تمام پالسهای موجرود در یرک پتابخانره را‬
‫نمز مشاهده پنمم (برای مثال ‪ .)datetime.date‬بنابراین هنگرامغ پره از ایرن ترابع بررای‬
‫پالس ‪ datetime.date‬استفاده پنمم‪ ،‬خروجغ زیر به ما نمایی داده مغشود‪.‬‬

‫)‪>>> dir(datetime.date‬‬
‫‪['__add__', '__class__', ..., 'day', 'fromordinal',‬‬
‫‪'isocalendar', 'isoformat', 'isoweekday', 'max',‬‬
‫‪'min', 'month', 'replace', 'resolution', 'strftime',‬‬
‫]'‪'timetuple', 'today', 'toordinal', 'weekday', 'year‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬این تابع یرک دیرد پلرغ نسربت بره پالسهرا و توابرع‬
‫موجود در یک ماژول را به ما مغدهد‪.‬‬
‫گاهغ اوقات ممان است اطالعات نمایی داده شده‪ ،‬بسمار طوالنغ بوده و نمازی بره‬
‫بسماری از آنها نباشد‪ .‬بدین منظور از ترفندی استفاده مغپنمم تا تنها اطالعات مربوط به‬
‫مواردی په نماز داریم به ما نشان داده شود‪.‬‬

‫])(‪>>> [_ for _ in dir(datetime) if 'date' in _.lower‬‬


‫]'‪['date', 'datetime', 'datetime_CAPI‬‬
‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪283 ‬‬

‫در مثال باال با نوشتن یک لمست با حلقهی ‪ for‬و شرط ‪ if‬در یک خط‪ ،‬تنها مرواردی‬
‫را از خروجغ دستور )‪ dir(datetime‬استخراج پرردیم پره در آنهرا نرام ‪ date‬وجرود‬
‫دارد‪ .‬توجه داشته باشمد په استفاده از متد ‪ lower‬بدین منظور است په جسرت و جروی‬
‫ما حساس به حروف پوچک و بزرگ نباشد‪.‬‬
‫گاهغ اوقات دریافت تنها لمستغ از ویژگغهای موجود در یک ماژول ممارن اسرت‬
‫پماغ به ما ناند‪ .‬بنابراین نماز اسرت ترا اطالعرات بمشرتری در مرورد هرپردام از آنهرا‬
‫بدست آورده و با نحوهی پار با آنها آشنا شویم‪.‬‬
‫با استفاده از تابع ‪ help‬در پرایتون مغتروانمم اطالعرات جرامعغ در مرورد تمرام اشرماء‬
‫موجود در پایتون بدست آوریم‪.‬‬

‫)‪>>> help(datetime‬‬

‫با اجرای دستور باال در مفسر پایتون‪ ،‬اطالعاتغ در مرورد مراژول ‪ datetime‬بره شرما‬
‫نمایی داده مغشود‪.‬‬

‫‪Help on module datetime:‬‬

‫‪NAME‬‬
‫‪datetime - Fast implementation of the datetime‬‬
‫‪type.‬‬
‫‪CLASSES‬‬
‫‪builtins.object‬‬
‫‪date‬‬
‫‪datetime‬‬
‫‪time‬‬

‫بدین ترتمرب شرما مغتوانمرد مسرتندات پراملغ در مرورد مراژول ‪ datetime‬بدسرت‬


‫آورید‪ .‬برای خروج از این محمط پافغ است تا از پلمد ‪ q‬استفاده پنمد‪.‬‬
‫شما مغتوانمد تابع ‪ help‬را برای سایر اشما موجود در پایتون نمز اسرتفاده پنمرد‪ .‬بردین‬
‫صورت مفسر پایتون به طور خودپار مستنداتغ برای شرما آمراده پررده و بره شرما نشران‬
‫مغدهد‪ .‬شما برای پالسهایغ په خود ایزاد مغپنمد نمز مغتوانمد از تابع ‪ help‬استفاده‬
‫‪  284‬ترفندهای پایتون‬

‫نمایمد‪ .‬این تابع در صورت وجود ‪ docstring‬در پالس شما‪ ،‬آن را نمایی خواهرد داد‪.‬‬
‫در ادامه سایر موارد استفاده از این تابع را مغتوانمد مشاهده پنمد‪.‬‬

‫)‪>>> help(datetime.date‬‬
‫)‪>>> help(datetime.date.fromtimestamp‬‬
‫)‪>>> help(dir‬‬

‫طبمعتررا بررا پمررک موتورهررای جسررت و جررویغ ماننررد ‪ Google‬و سررایتهایغ ماننررد‬
‫‪ ،Stackoverflow‬اطالعات جامع ترری نسربت بره اسرتفاده از تروابعغ ماننرد ‪ dir‬و ‪help‬‬
‫مغتوانمد بدست آورید‪ .‬اما این توابع نمز گراهغ اوقرات بررای بدسرت آوردن اطالعراتغ‬
‫مختصر و زمانغ په دسترسغ به اینترنت ندارید‪ ،‬مغتوانند مفمد واقع شوند‪.‬‬

‫‪ .1-1-8‬نکات کلیدی در مورد ماژولها در پایتون‬


‫با استفاده تابع ‪ dir‬در پایتون مغتوانمم ویژگغها‪ ،‬توابع و پالسهای موجود در یرک‬
‫ماژول را مشاهده پنمم‪ .‬همچنمن با پمک تابع ‪ help‬نمز مغتوان مستندات مختصرری در‬
‫مورد هر پدام از ماژولها و حتغ سایر اشماء موجود در پایتون بدست آورد‪.‬‬

‫‪ .2-8‬ایزوله سازی محیط پروژه با کمک ‪Virtualenv‬‬

‫زبان برنامه نویسغ پایتون دارای یرک سمسرتم یاپارچره سرازی بسرتهها اسرت پره برا‬
‫استفاده از آن مغتوانمد بستههای مرورد نمراز خرود را پره بره پمرک ابرزار ‪ pip‬نصرب و‬
‫مدیریت پنمد‪ .‬یاغ از مشاالت موجود در هنگام نصرب یرک بسرته‪ ،1‬ا رر گرذاری آن‬
‫روی محمط ‪ global‬پایتون است‪ .‬بدین معنا په بستهی مورد نظر روی پل سمسرتم عامرل‬
‫نصب مغشود‪.‬‬
‫مشال زمانغ رخ مغدهد په چندین پروژه در سمستم خود داشته باشمم په هر پردام‬
‫از آنها از نسخهی متفاوتغ از یک پامج اسرتفاده مغپننرد‪ .‬بررای مثرال یرک پرروژه برا‬
‫‪ Django 2.2‬پار مغپند؛ در صورتغ په پروژهی دیگر نماز به ‪ Django 3.0‬دارد‪.‬‬

‫‪1 package‬‬
‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪285 ‬‬

‫هنگررامغ پرره یررک بسررته را برره صررورت ‪ global‬روی سمسررتم نصررب مغپنمررد‪ ،‬تنهررا‬
‫مغتوانمد یک نسخه از هر بسته روی سمستم خود داشرته باشرمد پره مغتوانرد مشراالتغ‬
‫برای شما در آینده به وجود آورد‪.‬‬
‫حتغ ممان است پروژههایغ روی سمستم خود داشته باشمد په هرپردام برا نسرخهی‬
‫مختلفغ از پایتون پار مغپنند‪ .‬برای مثال برنامهای قدیمغ روی سمستم شما وجرود دارد‬
‫په هنوز به توسعهی آن مشغولمد و نماز به استفاده از پایتون ‪ 2‬دارید‪ .‬در حرالغ پره سرایر‬
‫پروژههای شما از پایتون ‪ 3‬استفاده مغپنند‪ .‬یا حتغ شراید پرروژهای نمراز بره پرایتون ‪3.5‬‬
‫داشته و پروژهای دیگر به پایتون ‪ 3.7‬نماز داشته باشد‪.‬‬
‫عالوه بر این موارد‪ ،‬نصب یک بسته به صورت ‪ global‬ممان است مشاالتغ امنمتغ‬
‫روی سمستم عامل ایزاد پند‪ .‬چرا په برای نصب یک بسته به این صورت‪ ،‬نماز است ترا‬
‫شما دسترسغ سطح باال (‪ root‬یا ‪ )admin‬در سمستم داشته باشرمد؛ و زمرانغ پره دسرتور‬
‫‪ pip install‬را اجرا مغپنمد‪ ،‬پامج شما از اینترنت دانلود شده و نصب مغشود‪.‬‬

‫‪ .1-2-8‬استفاده از محیط مجازی‪ 1‬در پروژهها‬


‫راه حل مشال باال‪ ،‬جدا سازی محمط پروژهی خود با پمرک یرک محرمط مزرازی‬
‫(‪ )Virtual Environment‬است‪ .‬بدین ترتمب شما مغتوانمد بستههای مورد نماز خرود را‬
‫در هر پروژه جدا پرده و حتغ از نسخههای مختلفغ از پایتون برای برنامههایتان اسرتفاده‬
‫پنمد‪.‬‬
‫در واقع یک ‪ ،Virtual Environment‬یک محرمط پرامالد ایزولره اسرت و در همران‬
‫مسمر پروژهی ما قرار مغگمرد‪ .‬برای شفاف سازی بمشتر با ایرن موضرو ‪ ،‬در ادامره یرک‬
‫محمط مزازی (یا به اختصار ‪ )virtualenv‬ساخته و سپس بسرتههایغ را بررای آن نصرب‬
‫مغپنمم‪.‬‬

‫‪1 Virtual Environment‬‬


‫‪  286‬ترفندهای پایتون‬

‫ابتدا بمایمد تا ببمنمم محمط پایتون ‪ global‬مرا در چره مسرمری قررار دارد‪ .‬بررای اینارار‬
‫مغتوانمم از دستور ‪ which‬در سمستم عاملهای ‪ Linux‬یا ‪ macOS‬استفاده پرده تا مسرمر‬
‫مدیر بستهی‪ pip 1‬را پمدا پنمم‪.‬‬

‫‪$ which pip3‬‬


‫‪/usr/local/bin/pip3‬‬

‫در اپثر مواقع پمشنهاد مغشود تا محمط ایزولهی خود را در همان مسمر پروژهی خود‬
‫بسازید‪ .‬برای ساخت یک ‪ virtualenv‬از دستور زیر استفاده مغشود‪.‬‬

‫‪$ python3 -m venv ./venv‬‬

‫با اجرای این دستور‪ ،‬یک مسمر با نام ‪ venv‬برای شما ساخته مغشود په ایرن محرمط‬
‫ایزوله از پایتون ‪ 3‬استفاده مغپند‪.‬‬

‫‪$ ls venv/‬‬
‫‪bin‬‬ ‫‪include‬‬ ‫‪lib‬‬ ‫‪pyvenv.cfg‬‬

‫حال اگر دوباره از دستور ‪ which‬برای پمدا پردن نسخهی فعرال ‪ pip‬اسرتفاده پنرمم‪،‬‬
‫دوباره همان خروجغ ‪ global‬مانند حالت قبل به ما نمایی داده مغشود‪.‬‬

‫‪$ which pip3‬‬


‫‪/usr/local/bin/pip3‬‬

‫این خروجغ بدان معناست په هنوز هم اگر پامزغ را با ‪ pip‬نصرب پنرمم‪ ،‬آن بسرته‬
‫به صورت ‪ global‬روی سمستم نصب مغشود‪ .‬ما یک گام دیگر برای استفاده از محرمط‬
‫ایزوله ی خود فاصله داریم و آن هرم فعرال سرازی آن اسرت‪ .‬بررای فعرال سرازی محرمط‬
‫‪ virtualenv‬په دقایقغ پمی ایزاد پردیم نماز است تا دستور زیر را وارد پنمم‪.‬‬

‫‪$ source ./venv/bin/activate‬‬


‫‪(venv) $‬‬

‫‪1 Package Manager‬‬


‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪287 ‬‬

‫با اجرای فایل ‪ activate‬در ‪ ،virtualenv‬محمط مزازی ما فعال شرده و از ایرن پرس‬
‫تمام دستورات پایتونغِ ما تنها روی همرمن محرمط ترا مر مغگذارنرد‪ .‬بررای تصردیق ایرن‬
‫موضو مغتوانمم دوباره از دستور ‪ which‬برای ‪ pip‬استفاده پنمم‪.‬‬

‫‪(venv) $ which pip3‬‬


‫‪/Users/dan/my-project/venv/bin/pip3‬‬

‫همانطور په مشاهده مغپنمد‪ ،‬از این پس با اجرای دستور ‪ ،pip‬نسرخهی موجرود در‬
‫‪ virtualenv‬فراخروانغ مغشرود و دیگرر بسرتهای بره صرورت ‪ global‬در سمسرتم نصررب‬
‫نخواهد شد‪ .‬هممن حالت برای اجرای دستور ‪ python‬نمز صردق مغپنرد‪ .‬برار دیگرر برا‬
‫پمک دستور ‪ which‬قصد داریم بفهممم پره آیرا در هنگرام اجررای مفسرر ‪ ،python‬از‬
‫محمط مزازی ما استفاده مغشود یا خمر‪.‬‬

‫‪(venv) $ which python‬‬


‫‪/Users/dan/my-project/venv/bin/python‬‬

‫البته توجه داشته باشمد په محمط ما در حال حاضر پامالد خالغ بوده و بسرتهای بررای‬
‫محمط پایتونغ ما نصب نشده است‪ .‬با اجرای دستور زیر مغتوانمم مشاهده پنمم پره تنهرا‬
‫بستههای پایهای و پمی فرض در محمط ایزوله شدهی ما وجود دارد‪.‬‬

‫‪(venv) $ pip list‬‬


‫)‪pip (9.0.1‬‬
‫)‪setuptools (28.8.0‬‬

‫به منظور نصب یک بسرته در محرمط ‪ ،virtualenv‬مغتروانمم از دسرتور ‪pip install‬‬


‫>‪ <package‬استفاده پنمم‪ .‬در قطعه پد زیر بستهای با نام ‪ schedule‬را نصب مغپنمم‪.‬‬

‫‪(venv) $ pip install schedule‬‬


‫‪Collecting schedule‬‬
‫‪Downloading schedule-0.4.2-py2.py3-none-any.whl‬‬
‫‪Installing collected packages: schedule‬‬
‫‪Successfully installed schedule-0.4.2‬‬
‫‪  288‬ترفندهای پایتون‬

‫همانطور په مشاهده مغپنمد‪ ،‬دیگر نمازی بره دسترسرغ ‪ admin/root‬بررای اجررای‬


‫این دستور نمغباشد‪ .‬همچنمن زمانغ په بخواهمد ایرن بسرته را برروز رسرانغ پنمرد‪ ،‬تنهرا‬
‫هممن بستهی موجود در این محمط مزازی بروز مغشود‪ .‬به معنرای دیگرر نمازمنردیهای‬
‫پروژههای شما در هر محمط مزازی از یادیگر جدا بوده (حتغ با محمط ‪ )global‬و همک‬
‫ارتباطغ با یادیگر ندارند‪.‬‬
‫حال اگر دوباره دستور ‪ pip list‬را اجرا پنمم‪ ،‬مغبمنمم په بستهی ‪ schedule‬نمز به ما‬
‫نمایی داده مغشود‪.‬‬

‫‪(venv) $ pip list‬‬


‫)‪pip (9.0.1‬‬
‫)‪schedule (0.4.2‬‬
‫)‪setuptools (28.8.0‬‬

‫اگر در این حالت یک فایل پایتونغ (فایلغ با پسوند ‪ )py‬را اجرا پنمم یا برای هر پار‬
‫دیگری از دستور ‪ python‬اسرتفاده پنرمم‪ ،‬از نسرخهی پرایتون موجرود در همرمن محرمط‬
‫مزازی و بستههای نصب شده برای آن استفاده مغشود‪.‬‬
‫اما چگونه مغتوان از این محمط خارج شد؟ همانند دستور ‪ ،activate‬دستوری با نرام‬
‫‪ deactivate‬نمز وجود دارد په با اجرای آن مغتوانمم از محمط ‪ venv‬خارج شویم‪.‬‬

‫‪(venv) $ deactivate‬‬
‫‪$ which pip3‬‬
‫‪/usr/local/bin‬‬

‫به طور پلغ استفاده از ‪ virtual environment‬باعث مغشود تا بهتر بتوانمد بستههای‬
‫پایتونغ خود را با توجه به نمازتان در هر پروژه به صورت مززا مردیریت پنمرد‪ .‬بنرابراین‬
‫هممشه پمشنهاد مغشود تا برای تمام پروژههایتران از یرک محرمط مزرازی جردا اسرتفاده‬
‫پنمد‪.‬‬
‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪289 ‬‬

‫همچنمن توجه داشته باشمد په برای پروژههای خود یرک فایرل ‪requirements.txt‬‬
‫ایزاد پرده و تمام بستههای نصب شده برای پروژهی خود را در آن ذپر پنمد‪ .‬برای این‬
‫پار مغتوانمد از دستور زیر استفاده پنمد‪.‬‬

‫‪(venv) $ pip freeze > requirements.txt‬‬

‫از این پس هر برنامه نویس دیگری مغتواند با ایزاد یک ‪ ،virtualenv‬تنها با اجرای‬


‫دستور زیر بستههای مورد نماز پروژه را برای خود نصب پند‪.‬‬

‫‪(venv) $ pip install –r requirements.txt‬‬

‫‪ .2-2-8‬نکات کلیدی در هنگام ایزوله سازی پروژههای پایتونی‬


‫استفاده از ‪ virtuale environment‬باعث مغشود تا نمازمندیهای پروژههرای مرا از‬
‫یادیگر جدا شوند‪ .‬به منظور جلوگمری از تداخل در نسخههای مختلفغ از زبران پرایتون‬
‫یا نسخههای مختلف یک بسته‪ ،‬ساخت یک محمط مزازی مززا برای هر پروژه پمشرنهاد‬
‫مغشود‪ .‬این عمل به مدیریت بهتر بستههای شما در آینده بسمار پمک مغپند‪.‬‬

‫‪ .3-8‬نگاهی به پشت پردهی بایت کدها‪ 1‬در پایتون‬


‫هنگامغ په مفسر ‪ CPython‬برنامهی شما را اجرا مغپند‪ ،‬ابتدا تمام برنامه به دنبالهای‬
‫از بایت پدها ترجمه مغشود‪ .‬در واقع بایت پد‪ ،‬یک زبان واسطه برای پایتون بروده پره‬
‫به بهمنه سازی پد شما پمک مغپند‪.‬‬
‫این پار باعث صرفه جویغ در زمان و حافظه در هنگام اجرای دوبارهی پد مغشود‪.‬‬
‫برای مثال‪ ،‬بایت پدهای حاصل از مرحلهی پامپایل‪ ،‬روی دیسک با نامهایغ ماننرد ‪pyc‬‬
‫یا ‪ pyo‬پی شده تا اجرای دوبارهی پدها سریعتر شود‪.‬‬
‫تمام این مراحل از چشم برنامه نویس پنهان است‪ .‬چراپه برنامه نویس الزم نمست ترا‬
‫از مراحل و چگونگغ تفسمر پدهای خود اطال داشته باشد‪ .‬امرا برا ایرن حرال مرا قصرد‬

‫‪1 bytecodes‬‬
‫‪  290‬ترفندهای پایتون‬

‫داریم تا دریابمم په مفسر ‪ CPython‬چگونه ایرن پرار را انزرام مغدهرد‪ .‬گراهغ اوقرات‬
‫درک چگونگغ پارپرد اجزاء درونغ پایتون‪ ،‬به ما پمک مغپند تا پدهای خود را به‬
‫صورتغ بهمنه تر بنویسمم‪.‬‬
‫در ادامه یک تابع با نام ‪ greet‬پماده سازی خواهمم پرد و با استفاده از آن در ادامهی‬
‫این بخی‪ ،‬قصد داریم تا چگونگغ پارپرد بایت پدها در پایتون را دریابمم‪.‬‬

‫‪def greet(name):‬‬
‫'!' ‪return 'Hello, ' + name +‬‬

‫)'‪>>> greet('Guido‬‬
‫'!‪'Hello, Guido‬‬

‫همانطور په اشاره پرده بودیم‪ CPython ،‬پد نوشته شردهی مرا را قبرل از اجررا‪ ،‬بره‬
‫یک زبان ممانغ ترجمه مغپند‪ .‬خب اگر این ادعا درست باشد‪ ،‬ما بایرد بتروانمم نتمزرهی‬
‫حاصل از مرحلهی پامپایل را مشاهده پنمم‪.‬‬
‫هر تابع در پایتون دارای ویژگغ __‪ __code‬اسرت پره برا اسرتفاده از آن مغتروانمم‬
‫دستور العملهای مربوط به ماشمن مزازی پایتون‪ ،1‬ابتها‪ 2‬و متغمرهایغ‪ 3‬په در ترابع مرا‬
‫استفاده مغشود را مشاهده پنمم‪.‬‬

‫‪>>> greet.__code__.co_code‬‬
‫'‪b'd\x01|\x00\x17\x00d\x02\x17\x00S\x00‬‬
‫‪>>> greet.__code__.co_consts‬‬
‫)'!' ‪(None, 'Hello, ',‬‬
‫‪>>> greet.__code__.co_varnames‬‬
‫)‪('name',‬‬

‫همانطور په مشاهده مغپنمد‪ co_consts ،‬شامل بخشغ از رشتهی برگشت داده شده‬
‫در تابع است‪ .‬همانطور په از نام ‪ constants‬مشخص است‪ ،‬آنها مقادیر ابتغ هسرتند و‬

‫‪1 Virtual Machine Instructions‬‬


‫‪2 Constants‬‬
‫‪3 Variables‬‬
‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪291 ‬‬

‫در برنامه تغممر نمغپنند‪ .‬این مقادیر به منظور صرفه جرویغ در مصررف حافظره‪ ،‬جردا از‬
‫‪ code‬و ‪ varnames‬نگهداری شدهاند‪.‬‬
‫بنابراین زبان پایتون بزای تارار مقادیر ابت در ‪ ،co_code‬آنها را در ماانغ مزرزا‬
‫نگهداری مغپند؛ و از این پس ‪ co_code‬مغتواند با جست و جو در فهرست ابتهرا‪،‬‬
‫آنها را بمابد و در برنامه جای دهد‪ .‬هممن عمل برای مقادیر ‪ co_varnames‬نمرز صردق‬
‫مغپند‪.‬‬
‫هنوز هم با مشاهدهی خروجغ ‪ co_code‬ناتهی خاصغ دسرتگمرمان نمغشرود‪ .‬ایرن‬
‫خروجغ در واقع زبانغ برای ارتباط با ماشمن مزازی پایتون بوده و قابل خوانردن نمسرت‪.‬‬
‫توسعه دهندگان ‪ CPython‬ایرن زبران را درک مغپننرد‪ .‬بنرابراین آنهرا ابرزاری برا نرام‬
‫‪ disassembler‬برای ما فراهم پردهاند په با پمرک آن مغتروانمم ایرن بایرت پردها را‬
‫بهتر درک پنمم‪.‬‬
‫دیس اسمبلر (‪ )disassembler‬در پایتون در ماژولغ با نام ‪ dis‬قرار دارد‪ .‬بنرابراین بره‬
‫راحتغ مغتوانمم از این ماژول و تابع ‪ dis.dis‬موجود در آن به منظرور درک بهترر بایرت‬
‫پدهای تابعغ په نوشتهایم استفاده پنمم‪.‬‬

‫‪>>> import dis‬‬


‫)‪>>> dis.dis(greet‬‬
‫‪2‬‬ ‫‪0 LOAD_CONST‬‬ ‫)' ‪1 ('Hello,‬‬
‫‪2 LOAD_FAST‬‬ ‫)‪0 (name‬‬
‫‪4 BINARY_ADD‬‬
‫‪6 LOAD_CONST‬‬ ‫)'!'( ‪2‬‬
‫‪8 BINARY_ADD‬‬
‫‪10 RETURN_VALUE‬‬

‫در واقع این پتابخانه دستورالعملهای موجود را به بخیهای مختلفغ تقسمم پرده و‬
‫داده است‪ .‬همچنمن مغتوانمد مشاهده پنمد په‬ ‫به هرپدام یک آپاد مشخص اختصا‬
‫مراجررع مربرروط برره ابتهررا و متغمرهررا بررا بایررت پرردها ترپمررب شررده و ‪ co_consts‬و‬
‫‪ co_varnames‬به صورتغ واضح به ما نشان داده شده است‪.‬‬
‫‪  292‬ترفندهای پایتون‬

‫با نگاهغ دقمق تر به آپادهای ارایه شرده‪ ،‬مغتروانمم دریرابمم پره ‪ CPython‬چگونره‬
‫دستورات نوشته شده در تابع ‪ greet‬را اجرا مغپند‪ .‬در واقع ‪ CPython‬ابتدا مقدار ابت‬
‫) ‪ (‘Hello’,‬در اندیس ‪ 1‬را دریافت پرده و آن را در یک پشته قرار مغدهد‪ .‬سپس متغمر‬
‫‪ name‬را دریافت پرده و آن را نمز به پشته اضافه مغپند‪.‬‬
‫ساختار دادهی پشته (‪ )stack‬برای فضرای ذخمرره سرازی درونرغ در ماشرمن مزرازی‬
‫پایتون بسمار مورد استفاده قرار مغگمرد‪ .‬پالسهای متنوعغ از ماشمن مزازی در پرایتون‬
‫وجود دارد په یاغ از آنها ماشمن پشرته اسرت‪ .‬ماشرمن مزرازی ‪ CPython‬نمونرهای از‬
‫پماده سازی یک ماشمن مزازی است‪ .‬ما در این بخی وارد جزیمات مربوط به این مسائل‬
‫نخواهمم شد‪ .‬خواندن تئوریهای مربوط به ماشمن مزازی بسمار مفصل بوده پره در ایرن‬
‫پتاب نمغگنزد‪.‬‬
‫آنچه په در مورد پشته به عنوان یک ساختار داده جالب اسرت‪ ،‬پشرتمبانغ آن تنهرا از‬
‫دو عمل ‪ push‬و ‪ pop‬است‪ .‬عمل ‪ push‬مقداری را به باالی یرک پشرته اضرافه پررده و‬
‫عمل ‪ pop‬آخرین مقدار اضافه شده بره براالی پشرته را برمغگردانرد‪ .‬بررخالف سراختار‬
‫دادهی آرایه‪ ،‬اماان دسترسغ به سایر عناصر موجود پایمن تر از عنصر روی پشرته وجرود‬
‫ندارد‪.‬‬
‫فرض مغپنمم په در ابتدا پشتهی موجود خالغ است و سپس دو آپاد ابتدایغ اجررا‬
‫مغشود‪ .‬پس از اجرای آنها‪ ،‬پشتهی ما همانند زیر مغشود‪.‬‬

‫)"‪0: 'Guido' (contents of "name‬‬


‫' ‪1: 'Hello,‬‬

‫سررپس دسررتور ‪ BINARY_ADD‬دو مقرردار برراالی پشررته را برداشررته و برره یارردیگر‬


‫مغچسباند‪ .‬حال دوباره مقدار حاصل را روی پشته قرار مغگمرد‪.‬‬

‫'‪0: 'Hello, Guido‬‬


‫فصل هشتم‪ :‬تکنیکهایی برای بهره وری بیشتر از زبان پایتون ‪293 ‬‬

‫پس از آن دوباره دستور ‪ LOAD_CONST‬اجرا شده و مقدار ابت بعدی به روی‬


‫پشته اضافه مغشود‪.‬‬

‫'!' ‪0:‬‬
‫'‪1: 'Hello, Guido‬‬

‫حررال دوبرراره دسررتور آپاررد ‪ BINARY_ADD‬اجرررا شررده و دو مقرردار روی پشررته برره‬
‫یادیگر متصل مغشود په حاصل آن رشتهی نهایغ ما خواهد بود‪.‬‬

‫'!‪0: 'Hello, Guido‬‬

‫آخرین دستور بایت پد ‪ RETURN_VALUE‬است په از ماشمن مزرازی مقردار روی‬


‫پشته را درخواست پرده و آن را به عنوان مقدار بازگشتغ تابع‪ ،‬به برنامه برمغگرداند‪.‬‬
‫همانطور په در مثرال براال مشراهده پردیرد‪ ،‬نحروهی اجررای ترابع ‪ greet‬در ماشرمن‬
‫مزازی ‪ CPython‬را به سادگغ شبمه سازی نمودیم‪.‬‬
‫مبحث ماشمنهای مزازی در پایتون بسمار طوالنغ و مفصل اسرت پره در چرارچوب‬
‫این پتاب نمغگنزد‪ .‬پمشنهاد مغپنمم تا در آینده در مورد این مبحرث مطالعره پنمرد ترا‬
‫بمشتر با زبان شمرین پایتون و الیههای پایمن آن آشنا شوید‪.‬‬

‫‪ .1-3-8‬نکات کلیدی در هنگام کار با بایت کدها در پایتون‬


‫مفسر پایتون ابتدا برنامههای نوشته شدهی ما را به زبانغ واسطه ترجمه پرده و سرپس‬
‫به اجرای دستورات بایت پد روی یک ماشمن مزازی پشتهای مغپردازد‪.‬‬
‫با استفاده از ماژول ‪ dis‬در پایتون مغتوان چگونگغ اجرای دسرتورات بایرت پرد در‬
‫پشت صحنهی تمام برنامهها را به سادگغ مشاهده نمود‪.‬‬

You might also like