Python Tricks Boby - Cloud
Python Tricks Boby - Cloud
لطفا برا ی حمایت از حفظ حقوق نویسندگان و مصنفان از هرگونه نشرر و پپرغ رمرمزراز ایرن ا رر
خودداری پنمد .حمایت شما موجب تولمد محتوای با پمفمت توسط جامعهی نویسندگان خواهد شد.
در صورتغ په این پتاب از روشهای دیگرری رمرر از وبسرایت رسرمغ متررجممن بره دسرت شرما
رسمده است ،لطفا اقدام به خرید و مطالعهی نسخه قانونغ خود پنمد.
https://boby.cloud https://devmo.in
ترفندهای اپیتون
مترجمین:
سید محمد بابازاده
سید معین باباپور
ناشر :پژوهندگان راه دانش
شماره نشر1499 :
نام کتاب :ترفندهای پایتون
مترجمین :سیدمحمد بابازاده -سید معین باباپور
طراح جلد :امیرمحمد حسینی پور
تیراژ1000 :
نوبت چاپ :اول 1399
چاپ :ترنگ
صفحهآرایی :پژوهندگان
سایز :وزیری
شابک978-600-345-496-5 :
قیمت 150000 :تومان
من عاشق این پتاب هستم .این پتاب همانند یک آموزش طبقه بندی شده ترفندهای
جالب پایتون است .من در حال یادگمری پرایتون هسرتم و برا پرمی زممنره 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 https://boby.cloud
2 https://devmo.in
فهرست مطالب
فصل اول
معـرفـی
.1-1ترفندهای پایتون چیست؟
ترفندهای پایتونغ یک قطعه پوتاه پد از زبان برنامه نویسغ پایتون است په به عنوان
ابزاری برای آموزش مفاهمم عممق استفاده مغشود .ترفند پایتون جنبه ای از پرایتون را برا
یک مثال ساده آموزش مغ دهد .ترفند پایتون انگمزه ای در اختمار شرما قررار مغدهرد و
شما را قادر مغ سازد تا در زبان برنامه نویسغ پایتون عممق تر حفاری پنمد و یک درک
شهودی برای خود ایزاد پنمد.
ترفندهای پایتونغ به عنوان یک سری پوتاه تصاویر از پدها شرو شرد پره مرن در
یک هفته در تویمتر به اشتراک گذاشتم .در پمال تعزب من ،افراد بازخوردهای شگفت
انگمزی نشان دادنرد و بررای روزهرای متروالغ تاره پردهای مرن توسرط افرراد مختلرف
ریتویمت مغشد.
هرروز توسعه دهندگان بمشتری از من سؤال مغپردند په آیا روشغ بررای دسترسرغ
به پل سری آموزشغ وجود دارد یا نه .درواقع ،من فقرط چنرد مرورد از ایرن ترفنردها را
ارائه پرده بودم په موضوعات مختلف مربوط بره زبران برنامره نویسرغ پرایتون را شرامل
مغشد .پشت سری آموزشغ من یک طرح جامع وجود نداشرت و فقرط یرک آزمرایی
جالب برای تفریح در تویمتر بود .اما این سؤاالت از تاه پدهای من باعث شرد احسراس
پنم پدهای پوتاه و پاربردی من به عنوان ابزاری بررای تردریس عممرق زبران پرایتون،
ارزش سرمایهگذاری و پاوش را دارد .سرانزام تصممم گرفتم چند ترفند پایتونغ دیگرر
تهمه پنم و آن ها را در قالب یک سری ایممل با افراد به اشتراک بگرذارم .در طرغ چنرد
روز ،صدها توسعه دهنده پایتون در اشتراک ایممل من بت نرام پردنرد و ایرن برازخورد
برای من بسمار شگفت انگمز بود.
طغ روزها و هفته های بعد ،تعداد بسمار زیادی از توسعه دهندگان پایتون با من آشرنا
شدند .آنها از من تشار مغپردند زیرا ترفندهای من باعث شده بود توسرعه دهنردگان،
18ترفندهای پایتون
بخی هایغ از زبان برنامه نویسغ پایتون په در حال دست و پنزه نررم پرردن برا مفراهمم
آن بودند را به خوبغ درک پنند .من فارمغپردم این ترفندها فقط یک سری تصراویر
ساده از تاه پدهای پایتون هستند .اما بسماری از توسرعه دهنردگان از طریرق ایرن پردها
مفاهمم عممقغ را آموختند و مشاهده این بازخوردها برای من بسمار خوشحالپننده بود.
پس از این من تصرممم گررفتم تزربمراتم را در دیگرر زممنره هرای ترفنردهای پرایتون
افزایی دهم و آن را در یک سری 30عددی ایممل به اشتراک بگذارم .هرپدام از این
ترفندها دارای یک عنوان و یک تصویر از پد بود و من به زودی متوجه محدودیتهای
اشتراک گذاری در این قالب شدم .در آن زمان یک توسعه دهنده نابمنرا پرایتون بره مرن
ایممل داد و از ایناه نمغتوانست این تصاویر را برای خود بخواند نااممد شده بود.
واضح است برای ایناه من بتوانم محتوا را در دسترس مخاطبان گسرترده ترری قررار
دهم باید زمان بمشتری را روی آن صرف مغپردم .بنابراین ،من نشستم ترا دوبراره تمرام
ایمملهای ترفندهای پایتون را با متن ساده و با برجسته سازی مبتنغ بر HTMLبازنویسرغ
پنم .این بازنویسغ باعث شد پاس هایغ را دریافرت پرنم و توسرعه دهنردگان از ایناره
مغتوانستند تاه پدها را Copy/Pasteپنند و با آن پار پنند ،خوشحال بودند.
هرچه توسعه دهندگان بمشرتری در ایرن سرری ایممرل برت نرام مغپردنرد ،از طریرق
دریافت بازخوردها متوجه الگوی خاصغ شدم .برخغ از ترفندها بسمار انگمزشغ بودند و
افراد مفاهمم عممق را به خوبغ متوجه مغشدند ،اما برای ترفندهای پمچمده ترر راهنمرایغ
وجود ن داشت په منابع بمشتری را در اختمار توسعه دهندگان مشتاق قرار دهد و به درک
عممقتر آنها از زبان برنامه نویسغ پایتون جامه عمل بپوشاند.
بمایمررد فقررط بگررویمم پرره ایررن یاررغ دیگررر از رسررالتهای مررن در وبسررایت
realpython.comبود په به پمک آن بتروانم توسرعه دهنردگان بمشرتری را برا مفراهمم
پمچمده و عممق پایتون آشنا پرنم .بنرابراین مرن تصرممم گررفتم بهتررین و ارزشرمندترین
ترفندهای پایتون را از سری ایمملها جمع آوری پنم و با توضمحات بمشتر در قالب یک
پتاب ارزشمند منتشر پنم.
فصل اول :معرفی 19
پتابغ په با نمونه پدهای پوتاه و قابل هضم په جالرب تررین جنبره هرای زبران
برنامه نویسغ پایتون را آموزش مغدهد.
پتابغ په مانند بوفه ای از ویژگغ های بسمار جذاب زبان برنامره نویسرغ پرایتون
عمل مغپند و مغتواند سطح انگمزه شما را در هنگام توسعه پرایتون براال ببررد و
هربخی به صورت جداگانه قابل مطالعه باشد.
پتابغ په دست شما را مغگمرد و شما را به اعمراق زبران برنامره نویسرغ پرایتون
مغبرد.
این پتاب شامل آزمایشات و تزربمات بسمار من در زبان برنامه نویسغ پایتون است و
از عشق من به این زبان برنامه نویسغ بره وجرود آمرده اسرت .اممردوارم پره شرما هرم از
خواندن این پتاب لذت ببرید و ترفندهای جدیدی را در رابطره برا زبران برنامره نویسرغ
پایتون یاد بگمرید.
این پتاب بررای پسرانغ پره قربال بررای مردتغ برا زبران پرایتون پرار پردهانرد و اپنرون
مغخواهند عممق تر با این زبان برنامه نویسغ آشنا شوند تا پدهایشان را سریع تر و بهمنره
تر بنویسند ،بسمار مناسب است و مغتواند باعث شود پدهای پایتونمک بهتری بنویسند.
فصل دوم
زمانغ په روی نامهای این لمست تغممر ایزاد پنمد ،خملرغ سرخت اسرت برا مشراهده
خروجغ دستور git diffبفهممد نامها چه تغممراتغ پردند .زیرا اپثر سمستم هرای پنتررل
پد منبع بر اساس شماره خط پد هستند و بره سرختغ مغتواننرد تغممررات در یرک خرط
واحد را برجسته پنند.
یک راه سریع حل این مشال ،اتخراذ یرک سربک پرد نویسرغ جدیرد اسرت پره در آن
آیتمهای لمست ،دیاشنری یا ست در پایتون را در چند خرط متروالغ بنویسرمد .هماننرد مثرال
زیر.
[ = >>> names
'Alice',
'Bob',
''Dilbert
]
اگر لمست خود را به صورت باال تعریف پنمد ،در هر خط یک مرورد وجرود دارد و
زمانغ په در سمستم پنترل پد منبع خود تغممرات را مشاهده پنمد ،بره راحترغ مغتوانمرد
متوجه شوید پدام نام ها اضافه ،حذف یا اصالح شده اند .ایرن یرک تغممرر پوچرک در
سبک پد نویسغ است امرا تزربره مرن نشران مغدهرد از اشرتباهات احمقانره جلروگمری
مغپند .اشتباهاتغ په ممان است برطرف شدن آن ساعت ها زمان ببررد .همچنرمن ایرن
تغممر باعث شد همتممغ های من بتوانند راحت تر پدهای من را بررسغ پنند.
24ترفندهای پایتون
اپنون دو مورد دیگر وجود دارد په ممان است باعث سردرگمغ شرود .هرر وقرت
یک مورد جدید را در انتهای لمست اضافه پنمد یا آخرین مورد را حذف پنمرد بایرد بره
صورت مداوم جای ویرگول را به روز رسانغ پنمد.
[ = >>> names
'Alice',
'Bob',
!'Dilbert' # <- Missing comma
''Jane
]
اگر قرار دادن عالمت ویرگول را فراموش پنمرد ،ممارن اسرت هنگرام اجررای پرد
متعزب شوید.
>>> names
]'['Alice', 'Bob', 'DilbertJane
همانطور په مشاهده مغپنمد ،پایتون در زمان اجرا ،رشته های Dilbertو Janeرا به
DilbertJaneتبدیل پرده و در واقع آنها را بهم چسباند .به این عمرل در پرایتون جمرع
بندی دقمق رشتهها 1مغگویند په یک رفتار طبمعغ در زبران پرایتون اسرت .امرا مغتوانرد
گاهغ اوقات با ایزاد مشاالتغ در برنامه پایتونغ ،شما را تا مرز پرتاب پردن مرانمتور از
پنزره به بمرون پمی ببرد.
با این وجود ،جمع بندی دقمرق رشرته هرا در پرایتون بسرمار مفمرد اسرت .بررای مثرال،
مغتوانمد از این روش برای زمانغ استفاده پنمد ،په مغخواهمد یک رشته مرتن طروالنغ
را در برنامه خود وارد پنمد اما نمغ خواهمد پره از عالمرت برک اسرلی (یرا \) اسرتفاده
پنمد.
از طرف دیگر هممن االن دیدیم په این قابلمت مفمد ،چطور مغتواند به یرک بحرران
در یک برنامه تزاری تبدیل شده و باعث ایزاد باگهایغ عزمب شود و به یرک بردهغ
فنغ تبدیل شود .حال چگونه این مشال را حل پنمم؟
اضافه پردن پامای گم شرده ،پرس از Dilbertباعرث مغشرود جلروی ایرن مشرال
گرفته شده و دو رشته Dilbertو Janeاز یادیگر جدا شوند.
[ = >>> names
'Alice',
'Bob',
'Dilbert',
''Jane
]
اما اپنون دور یک حلقه چرخمدیم و دوباره به نقطه ابتدایغ بازگشرتمم .بررای اضرافه
پردن یک نام جدید به لمست مزبور شدیم دو خط از پد را تغممر دهرمم .ایرن موضرو
باعث مغشود دیدن تغممرات پد منبع در خروجغ دستور git diffسخت تر شود .آیا نام
جدیدی اضافه شده است یا نام Dilbertتغممر پرده است؟
خوشبختانه راه های پایتونمک باعث مغشوند تا مشال قرار دادن پاما را یرک برار و
برای هممشه حل پنمم .برای حل دائمغ این مشال ،باید خود را آموزش دهمرد ترا یرک
سبک پدنویسغ مشخص را اتخاذ پنمد تا در وهله ی اول جلوی این ایزاد ایرن مشرال
گرفته شود .صبر پنمد تا به شما نشان دهمم چطور مغتوانمد این پار را انزام دهمد.
در پایتون ،مغتوانمد ویرگول را بعد از هر مورد در لمسرت ،سرت یرا دیاشرنری قررار
دهمد از جمله آخرین مورد موجود در پالاشرنهرای نرام بررده شرده .بنرابراین بره خرود
26ترفندهای پایتون
آموزش دهمد په همواره به یاد داشته باشمد انتهای خطوط خود را با ویرگرول بره اتمرام
برسانمد و از ایناه در خط انتهایغ ویرگول قرار ندهمد ،پرهمز پنمد.
برای مثال به قطعه پد زیر نگاه پنمد.
[ = >>> names
'Alice',
'Bob',
'Dilbert',
]
بعد از اسم Dilbertویرگول را مشاهده مغپنمد؟ این پار باعث مغشود بدون نماز به
جایگذاری مزدد ویرگول ،بتوانمد لمست خود را آپدیت پنمرد و خطروط پرد را ابرت
نگه دارید .این روش ،پنترل پد منبرع شرما را سرادهتر پررده و افرراد ،پردهای شرما را
خوشحالتر بازبمنغ مغپنند .گاهغ اوقرات جرادو در چمزهرای پوچرک اسرت ،درسرت
است؟
در این بخی مغخواهمم مقدمه ای برای استفاده از عبارت assertدر پایتون را بمران
پنمم .شما مغآموزید په چطور از عبارت assertبرای آشاارسازی خودپار خطاها در
پد پایتون استفاده پنمد .این ویژگغ باعث مغشود برنامه ی شما قابلمت اطممنان بمشرتری
داشته باشد و به راحتغ قابل دیباگ باشد.
در این مرحله ممان است برای شما سؤال ایزاد شود پره عبرارت assertionدقمقرا
چه چمزی هست و برای استفاده در چه جاهایغ مناسب است؟ بمایمد به این سؤال اساسغ
پاس دهمم.
در هسته زبان برنامه نویسغ پایتون ،عبارت assertیک ابزار مفمد برای دیباگ پدها
بوده په یک شرایط خاصغ را آزمایی مغپند .اگر مقردار عبرارت assertبرابرر true
شود ،اتفاقغ رخ نمغدهد و برنامه به خوبغ بره اجررای خرود ادامره مغدهرد .اگرر مقردار
عبارت assertبرابرر falseشرود ،خطرای AssertionErrorرخ داده و شرما بره راحترغ
خطاهای برنامه ی خود را پمدا خواهمد پرد.
پلمه assertدر لغت به معنغ ادعا پردن و دفا پردن اسرت .بنرابراین در پردهای
خود ادعا مغپنمد په شرایط خاصغ باید برقرار باشد ،در رمر این صورت با ایزاد یرک
خطا توسعه دهنده را مطلع خواهمد پرد.
به عبارت assertدر پد باال توجه پردید؟ نرخ تخفمف محاسبه شده توسط این تابع
نمغ تواند پمتر از صفر دالر و بمشتر از قممت اصلغ محصول باشرد .اگرر از ایرن قابلمرت
برای اعمال تخفمف معتبر استفاده پردیم اپنون باید اطممنران حاصرل پنرمم پره فرآینرد
مورد نظر به درستغ انزام مغشود.
برای مثال محصوالت فروشگاه به صورت دیاشرنری هرایغ سراده ذخمرره مغشروند.
شاید این روشغ نباشد په در یک برنامه واقعرغ بره پرار رود ،امرا مغتروان بررای ا برات
عملارد Assertionاز این روش استفاده پرد .بنابراین یک محصرول جدیرد مغسرازیم
په شامل 2مقدار نام محصرول (یرک جفرت پفری) و قممرت محصرول ( 14900دالر)
است.
اپنون بمایمد یک تخفمف 25درصدی به محصول اعمرال پنرمم و انتظرار داریرم پره
قممت محصول معادل 111.75دالر شود.
بسمارعالغ ،اپنون بمایمد یک مقدار تخفمف نامعتبر وارد پنمم .برای مثرال ،مرن %200
تخفمف به محصول اعمال مغپنم.
دهندگان به سرعت متوجه ریشه پدید آمدن خطا شوند و خطرا را سرریع ترر رفرع پننرد.
یک assertion errorدر پد شما رخ نمغدهد مگر در حالتغ په برنامهی نوشرته شرده
دچار خطا شود.
بمایمد یک نگاه نزدیرک ترر بره پارهرایغ پره مغتروانمم برا assertionانزرام دهرمم
بمندازیم و سپس دو اشرتباه مترداول هنگرام اسرتفاده از ایرن عبرارت در دنمرای واقعرغ را
بررسغ پنمم.
در عبارت فوق expression1 ،شررایطغ هسرت پره آن را تسرت مرغپنمم و مقردار
اختماری expression2پمامغ است په مغخواهمم زمانغ په خطا رخ داد نمایی دهرمم.
در زمان اجرای برنامه پایتونغ ،مفسر پایتون هرپدام از expressionها را به صورت زیر
پردازش مغپند.
if __debug__:
if not expression1:
)raise AssertionError(expression2
همچنررمن شررما مغتوانمررد از مقرردار expression2اسررتفاده پنمررد تررا یررک پمررام خطررا
اختصاصغ نمایی دهمد .این پار رفع مشال را راحت تر مغپند .برای مثال به قطعه پد
زیر دقت پنمد.
این پمام زشت است؟ موافقم ،اما بسمار پاربردی است زمانغ په شما با یرک خطرای
هایزنباگ 1در برنامه خود مواجه مغشوید.
1 Heisenbug
32ترفندهای پایتون
به تابع 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شروط موردنظر را چک پنمرد و در صرورتغ پره شررط هرا
نقض شدند ،یک خطای اختصاصغ تولمد پنمد.
در قطعه پد باال ،در زمان مناسب خطاهای AuthErrorو ValueErrorنمرایی داده
خواهند شد و شروط مورد نظر به خوبغ پنترل خواهند شد.
این مشال به این دلمل است په عبارات tupleپره خرالغ نمسرتند هممشره بره عنروان
trueدر زبان پایتون درنظر گرفته مغشوند .زمانغ په شما یک tupleرمرر خرالغ را بره
عبارت assertدر پایتون مغدهمد ،پاری پامالد بغ فایده انزرام دادهایرد ،زیررا همچگراه
خطایغ تولمد نخواهد شد و حاصل عبارت همواره صحمح است.
یک مثال دیگر را باهمدیگر مغبمنمم .فرض پنمد مرن برا خوشرحالغ تعردادی تسرت
نوشته ام و حس ایمنغ پاذبغ را در پدهایم ایزاد پررده ام .تصرور پنمرد از assertion
زیر در یاغ از تست ها استفاده پرده ام.
( assert
counter == 10,
''It should have counted all the items
)
در نگاه اول تست پامالد درست به نظر مغرسد .برا ایرن حرال ،هرگرز نتمزره درسرتغ
نخواهد داشت .مقدار assertionهمواره برابر با Trueارزیابغ مغشود .چرا ایرن اتفراق
فصل دوم :کدنویسی تمیز با پایتون 35
مغافتد؟ زیرا assertارزیابغ مغپند پره آیرا tupleدارای مقردار اسرت یرا نره و از آن
جایغ په دارای مقدار است ،پس نتمزه ارزیابغ همواره Trueاست.
همانطور په گفتم ،زمانغ په متوجه این حقمقت شدم ممخواستم خودپشغ پنم!
روشغ په مغتوانمد با این مشال مقابله پنمرد اسرتفاده از code linterاسرت .نسرخه
های جدید پایتون نمز دارای پمام اخطار برای این روشِ استفاده از assertionهستند.
به هرحال ،همواره دقت پنمد تست های برنامه باید ابتدا شاست بخورند و سپس پد
مربوط به تست ،نوشته شود .در رمر این صورت ممارن اسرت دچرار اشرتباهات مهلارغ
شوید همانند موردی په بررسغ پردیم.
عبارت assertباید تنها برای توسرعه دهنردگان باشرد و اسرتفاده از ایرن عبرارت
روش مناسبغ برای مدیریت خطاهای زمان اجرای برنامه نمست.
عبارت assertمغتواند به صورت سراسری در تنظممات مفسرر پرایتون رمرفعرال
شود.
به طور پلغ توصمه مغشود فایرل هرا را برا اسرتفاده از عبرارت withدر زبران برنامره
نویسغ پایتون باز پنمد .این پار باعث مغشود زمانغ په پارِ برنامه با فایل مورد نظرر بره
اتمام مغرسد ،ارتباط برنامه با فایل به صورت خودپار بسته شود .در حقمقت زمانغ پره
از عبارت withاستفاده مغپنمد پدهای شما به صرورتغ پره مشراهده مغپنمرد ،توسرط
مفسر پایتون ترجمه مغشود.
فصل دوم :کدنویسی تمیز با پایتون 37
ممان است بگویمد این پد پمغ طوالنغ است و پامالد درست اسرت .بره ایرن ناتره
توجه پنمد په استفاده از عبارت try ... finallyروشغ مو ر برای مردیریت خطرا اسرت.
برای مثال اگر برای این پار ،پدی شبمه به زیر بنویسمد ،دچار مشاالتغ خواهمد شد.
پماده سازی فوق تضممن نمغپند په هنگام نوشتن در فایرل ،اگرر برا خطرایغ مواجره
شدید پس از آن ارتباط با فایل توسط برنامه بسته شود و ممان است برنامه برا خطاهرای
متفاوتغ روبرو شود .به هممن دلمل اسرت پره در ایرن مواقرع از عبرارت 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باعررث مغشررود تررا از برره وجررود آمرردن حفررره امنمتررغ
جلوگمری شود .به دلمل ایناه هرزمان په پار برنامه با منابع تمام شد ،منابع را به صورت
خودپار آزاد مغپند.
class ManagedFile:
def __init__(self, name):
self.name = name
def __enter__(self):
)'self.file = open(self.name, 'w
return self.file
زبان برنامه نویسغ پایتون زمانغ ترابع __ __enterرا فراخروانغ مغپنرد پره برنامره
شرو به اجرای پدهای درون قسمت withمغپند و برنامره نمراز بره در اختمرار گررفتن
منابع دارد .هنگامغ په اجررای برنامره در قسرمت withبره پایران مغرسرد ،پرایتون ترابع
__ __exitرا فراخوانغ مغپند تا منابعغ په در اختمار گرفته شده بودند ،آزاد شوند.
نوشتن یک پالس از جنس context managerتنهرا راه بررای پشرتمبانغ از عبرارت
withدر پایتون نمست .همانطور په مشاهده پردید پالس ManagedFileتعرداد خرط
پدهای زیادی داشت و در زبان برنامه نویسغ پایتون هممشه به دنبال ساده ترین و بهترین
راه حل هستمم.
با استفاده از پتابخانه contextlibدر هسته زبان برنامه نویسغ پرایتون ،مغتروان یرک
الیه انتزاعغ بر روی پروتال context managerایزراد پررد .ایرن پرار ممارن اسرت
زندگغ را برای شما راحت تر پند!
40ترفندهای پایتون
برررای مثررال ،در قطعرره پررد زیررر بررا اسررتفاده از یررک decoratorپررایتون برره اسررم
contextmanagerمغتوان اماان پشتمبانغ از عبارت withرا به وجود آورد .در اینزرا
مثال قبلغ پالس ManagedFileرا بره صرورت ترابع بازنویسرغ مرغپنمم ترا ببمنرمم ایرن
تانمک چگونه پار مغپند.
@contextmanager
def managed_file(name):
try:
)'f = open(name, 'w
yield f
finally:
)(f.close
ویژگغ ها و ترفندهای پایتون است و مغتوانمد خملرغ سرریع بره مباحرث مختلرف گرذر
پنمد.
یک بار دیگر برای تاپمد مغگویم ،این په از پدام روش پماده سازی استفاده پنمرد
پامالد به ایناه شما و تممتان با پدام روش راحت تر است ،بستگغ دارد .هممشه راهرغ را
انتخاب پنمد په به خوانایغ پدهایتان افزوده شود.
پد باال شبمه زبان های 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
زیاد بد نمسرت ،درسرت اسرت؟ اممردوارم برا خوانردن ایرن قسرمت ،درک بهترری از
context managerها و عبارت withدر برنامه های پایتونغ بدست آوریرد .ایرن یرک
فصل دوم :کدنویسی تمیز با پایتون 43
ویژگغ جذاب در پایتون است په با آن مغتوانمد منابع سمستم را پایتونمک ترر مردیریت
پنمد.
زمانغ په عالمت _ به صورت پمشروند در ابتردای نرام متغمرهرا و توابرع پرایتون قررار
مغگمرد ،فقط به دلمل یک قرارداد بمن برنامره نویسران پرایتون اسرت .درواقرع ایرن یرک
قرارداد یا رسم ،بمن برنامه نویسان جامعه پایتون است و ترا مری در نحروه اجررای برنامره
شما ندارد.
استفاده از پمشوند _ در ابتدای نام متغمرها و توابع پایتون ،تنها برای راهنمرایغ توسرعه
دهندگان دیگر است و به این مفهوم است په متغمر یا تابع مورد نظر برای استفاده درونغ
در پدها در نظر گرفته شده است .این یک قرارداد است په در 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
# my_module.py:
def external_func():
return 23
def _internal_func():
return 42
اپنون ،به دلمل ایناه در ابتدای نام تابع عالمت _ قرار دادهایم ،اگر تمام توابرع را در
اساریپت دیگری importپنمم ،پایتون توابعغ په در ابتدای نام آنها عالمرت _ اسرت
را در اساریپت importنمغپند.
به این طریقهی ،importاصرالحا wildcard importگفتره مغشرود و بایرد ترا حرد
اماان از آن اجتناب پرد .زیرا با این پار مشخص نمست چه namespaceهرایغ درون
46ترفندهای پایتون
برنامه پایتون وارد مغشوند .بهتر است برای خوانایغ بمشتر پد ،بره صرورت دقمرق مروارد
importها مشخص شوند .هنگرامغ پره بره صرورت عرادی یرک مراژول را بره تنهرایغ
importمغپنمد ،قاعده فوق در توابعغ پره در ابتردای نرام آنهرا عالمرت _ قررار دارد
رعایت نمغشود و مغتوانمد توابع را فراخوانغ پنمد .به مثال زیر توجه پنمد.
مغدانم په ممان است در این جا پمغ گمج شده باشمد .اگر استاندارد توصمه شرده
استایل پدنویسغ PEP 8را رعایت پنمد و از wildcard importها اجتناب پنمرد ،تنهرا
پافغ است یک چمز را به یاد داشته باشمد:
استفاده از single underscoreدر ابتردای نرام متغمرهرا و توابرع پرایتونغ فقرط یرک
قرارداد بمن توسعه دهندگان به معنغ استفاده داخلغ متغمر یا ترابع اسرت .عردم فراخروانغ
این متغمرها و توابع توسط مفسر پرایتون اجبرار نمغشرود و تنهرا بررای راهنمرایغ توسرعه
دهندگان دیگر به منظور استفاده داخلغ متغمر یا تابع است.
گاهغ اوقات بهترین اسمغ په برای یک متغمر مغتوانمم انتخاب پنرمم توسرط هسرته
زبان برنامه نویسغ پایتون انتخاب شده است و نمغتوانمم از آن اسم استفاده پنرمم .بررای
مثال اسم هایغ نظمر classیا defرا نمغتوانمم برای یک متغمر پایتونغ انتخاب پنرمم .در
این مواقع مغتوانمد از یک عالمت _ در انتهای نام متغمر استفاده پنمد تا جلروی ترداخل
نامها را بگمرید.
فصل دوم :کدنویسی تمیز با پایتون 47
به صورت خالصه ،استفاده از عالمت _ در انتهای نام متغمرهرای پرایتونغ ،بره معنرای
جلوگمری از تداخل نام متغمر با نام های رزرو شده در زبان پایتون است و ایرن موضرو
در راهنمای استایل پدنویسغ PEP 8نوشته شده است.
الگوهایغ از نام گذاری پره تراپنون بررسرغ پرردیم ،ارلرب قرراردادی برمن توسرعه
دهندگان بودند .در رابطه با پالس های پرایتونغ پره ویژگغهرا و رفتارهرای پرالس برا
عالمت __ شرو مغشود ،قضمه پمغ متفاوت است.
زمانغ په از عالمت __ در ابتدای نام متغمرها و توابع یک پالس اسرتفاده مغپنمرد،
مفسر پایتون نام این صفات را تغممر مغدهد تا از تداخل نامها جلوگمری پند .به این پار
در پایتون 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
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
متغمرها و توابع ،بلاه در هر نامغ په با عالمت __ در فضرای یرک پرالس شررو شرود
توسط مفسر پایتون اعمال مغشود.
ظاهرا خملغ عممق شدیم .جالب است نه؟ حقمقت این است پره مرن ایرن مثرال هرا و
توضمحات را خملغ راحت از ذهن خود در نماورده ام .زمان و انرژی زیادی برای تحقمق
و درک درستغ از این ویژگغ از دست داده ام .من چندین سال است په از زبان پایتون
برای برنامهنویسغ استفاده مغپنم .اما درک نحوه عملارد مفسر پایتون در حالرت هرای
نماز به تحقمقات فراوانغ دارد. خا
گاهغ اوقات مهم ترین مهارت یک برنامه نویس ،تشخمص الگو و پمدا پردن جرایغ
است په باید مشال را در آنزا پمدا پند .اگر احساس مغپنمد پمغ در درک این مثال
ها گمج شده اید ،نگران همک چمز نباشمد .پمغ به خود زمان بدهمد و برا مثرال هرای ایرن
قسمت تمرین پنمد تا به خوبغ متوجه شوید.
1 Dunder
فصل دوم :کدنویسی تمیز با پایتون 53
برای مثال من نام __bazرا dunder bazمغخوانم .همچنمن نرام __ __initرا نمرز
dunder initمغخوانم .اگرچه ممان است بعضغ افراد فار پنند باید آن را به صورت
dunder init dunderبخوانند.
خب برویم سراغ نحوه نام گذاری بعدی ،یک قاعده نام گذاری دیگری وجود دارد
په برای پایتونمست ها شبمه به اسم رمز مغماند.
ممان است باعث شگفت زدگغ شما شود اما name manglingدر زمانغ په یرک
صفت پایتونغ با dunderشرو مغشود و پایان مغیابد ،اعمال نمغشود .متغمرهایغ پره
در ابتدا و انتهای آنها dunderقرار دارد توسط مفسر پایتون دست پاری نمغشوند.
class PrefixPostfixTest:
def __init__(self):
self.__bam__ = 42
__>>> PrefixPostfixTest().__bam
42
نام هایغ په در پایتون عالمت __ در ابتدا و انتهای آنها وجود دارد ،بررای اسرتفاده
در زبرران برنامرره نویسررغ پررایتون طراحررغ شررده انررد .ایررن قررانون در ترروابعغ مثررل خررا
__ __initپه تابع سازنده یک پالس است یا تابع __ __callپه برای فراخروانغ شرغ
استفاده مغشود یا توابع دیگر باار گرفته شده است.
این توابع به عنوان magic methodیا متدهای جادویغ توسط پرایتون معرفرغ شرده
اند ،اما بسماری از افراد جامعه پرایتون از جملره خرودم نرام متردهای جرادویغ را دوسرت
نداریم .این نام باعث مغشود بسماری از برنامه نویسان از اسرتفاده از ایرن قابلمرت دلسررد
شوند .در صورتغ په همک چمز جادویغ در این رابطه وجود ندارد .این یارغ از ویژگرغ
های هسته زبان برنامه نویسغ پایتون است په هر زمان نمراز باشرد ،بایرد بتوانمرد از آنهرا
استفاده پنمد.
54ترفندهای پایتون
به هر حال ،بهتر است متغمرهایغ په با عالمت __ شرو و پایان مغیابند نسازید تا از
تداخل پدهایتان در تغممرات آینده هسته زبان برنامه نویسغ پایتون جلوگمری پنمد.
همچنمن شما مغتوانمد از عالمت _ برای نام گذاری متغمرهرایغ پره برایتران اهممرت
ندارند هنگرام براز پرردن یرک پالاشرن پرایتون اسرتفاده پنمرد .تاپمرد مرغپنم ،ایناره
مغگویم متغمر اهممت ندارد تنهرا یرک رسرم برمن جامعره پرایتونغ اسرت .مفسرر پرایتون
هنگامغ په با "_" روبرو مغشود همک تغممری در نحوه ی اجرای برنامه انزام نمغدهرد.
مغتوانمد اینطور در نظر بگمرید په "_" تنها یک متغمر معتبر است په برای مواقعغ پره
نام متغمر برایمان اهممت ندارد از این نام استفاده مغپنمم.
برای مثال در پد زیر ،من یک tupleرا باز مرغپنم پره تنهرا عالقمنرد بره فملردهای
colorو mileageپالاشن tupleهستم و بقمه فملدها برایم مهم نمستند .به هر حرال ،بره
دلمل ایناه ترتمب هنگام باز پردن یک پالاشن مهم اسرت متغمرهرا را بره ترتمرب قررار
مغدهم و اینزا جایغ است په نام "_" به درد من مغخورد.
>>> color
''red
>>> mileage
فصل دوم :کدنویسی تمیز با پایتون 55
3812.4
_ >>>
12
در پنار ایناه "_" برای نام گذاری متغمرهرای مروقتغ اسرتفاده مغشرود "_" ،یرک
در Python REPLاست و از طریق آن مغتوان به نتمزه محاسبات قبلرغ متغمر مخصو
در interpreterپایتون دسترسغ داشت.
برای مثال هنگامغ په با سشن مفسر پایتون در حال پار هستمد و نماز به دسترسرغ بره
نتمزه محاسبه قبلغ را دارید مغتوانمد از "_" استفاده پنمد.
>>> 20 + 3
23
_ >>>
23
)_(>>> print
23
>>> )(list
][
>>> )_.append(1
>>> )_.append(2
>>> )_.append(3
>>> _
[1, ]2, 3
Single underscore در انتها :یک رسم نام گذاری برای جلروگمری از ترداخل
نام با پلمات پلمدی زبان برنامه نویسغ پایتون است و توسط مفسر پایتون پاری
انزام نمغشود.
Double underscore در ابتدا :باعث اعمال name manglingدر سرطح یرک
پالس مغشود و توسط مفسر پایتون تغممراتغ در نام گذاری انزام مغشود.
Double underscore در ابتدا و انتها :برای استفاده از توابع مشخص و تعریرف
شده در پایتون استفاده مغشود .از این حالت برای نام گذاری صفت هرای یرک
پالس استفاده نانمد تا از تداخل پمشگمری شود.
:Single underscore گاهغ اوقات بررای اسرتفاده از نرام هرای موقرت اسرتفاده
مغشود یا متغمرهایغ په نامشان برایمان مهم نمستند .همچنمن در سشرن Python
REPLبرای مشاهده نتمزه محاسبه قبلغ استفاده مغشود.
و بر پایه این متغمرها ،مایلمم رشته خروجغ را با پمام خطای زیر ایزاد پنمم.
بروز این خطا مغتواند واقعا صبح شنبه یک توسعه دهنده را خراب پند! اما امروز در
اینزا هستمم تا درباره قالببندی متن در پایتون صحبت پنمم .پرس بمایمرد بره پرار خرود
برسمم.
در اینزا از تعممنپننده قالب %sاستفاده مغپنمم تا به پایتون بگوید په مقردار متغمرر
nameرا بصورت رشته جایگزین پند .به این قالببندی متن به "سبک قردیمغ" گفتره
مغشود.
در قالببندی متن به سبک قدیمغ ،تعممنپنندههای دیگری از قالب نمرز وجرود دارد
پرره برره شررما اجررازه پنترررل رشررته خروجررغ را مررغدهررد .برررای مثررال ،تبرردیل اعررداد برره
هگزادسممال یا افزودن فضای خالغ در متن برای ایزاد جداول و گزارشهای قالببندی
شده زیبا ،اماانپذیر است.
در قطعه پد صفحهی بعد ،از تعممنپننده قالب %xبرای تبدیل مقدار از عدد صحمح
به رشته و نشان دادن آن بصورت هگزادسممال استفاده مغپنمم.
58ترفندهای پایتون
اگر بخواهمد جایگزینهای چندگانه را در رشته واحد ایزراد پنمرد ،سرمنتاس قالرب
بندی متن به سبک قدیمغ پمغ تغممر خواهد پرد .چون -٪اپراتور فقط یک آرگومران
را مغگمرد .شما باید طرف راست را در یک ،tupleمانند دستور زیر قرار دهمد.
همچنمن مغتوانمد قطعه پد باال را به صورت زیر بنویسمد تا عملمات جایگزینغ انزام
شود.
این پار باعث تغممر و اصالح سادهتر رشتهها در آینده مغشود و نمازی نمست نگرران
ترتمب جایگزینغ صحمح -٪اپراتور ها باشمد .البته نقطه ضعف این روش این است په به
پمغ تایپ بمشتری نماز دارد.
مطمئن هستم تعزب پردهاید په چرا این قالببندی به سبک printfقالببندی متن
به "سبک قدیمغ" ناممده مغشود .خب ،اجازه دهمد بره شرما بگرویم .سربک قردیمغ از
لحاظ فنغ پس از مدتغ ،به طور مزدد جایگزین قالببندی به "سبک جدید" شرد ،پره
در ادامه مغخواهمم پمرغ دربراره آن صرحبت پنرمم .برا ایناره قالرببنردی بره "سربک
قدیمغ" به طور پامل ترجمح داده نشده است ،اما پامالد هم پنار گذاشته نشرده و هنروز
در آخرین نسخههای پایتون پشتمبانغ مغشود.
فصل دوم :کدنویسی تمیز با پایتون 59
یا مغتوانمد طبق نام به جایگزینهای متغمرها رجو پرده و از آنها به هر ترتمرب پره
بخواهمد استفاده پنمد .این ویژگغِ پامالد قدرتمنردی اسرت ،چرون اجرازه تنظرمم مزردد
ترتمب نمایی بدون تغممر آرگومانهای ورودی به تابع formatرا مغدهد.
این روش همچنمن نشان مغدهد په سمنتاس برای قالببندی متغمر به صورت رشرته
هگزادسممال در برنامه تغممر پرده است .باید با افرزودن پسروند " ":xپرس از نرام متغمرر،
مشخصات قالب را منتقل پنمم.
در حالت پلغ ،سمنتاس جدید قالب بندی رشته ها ،سادهتر و قدرتمندتر شده است.
در پایتون ،3این قالببندی متن به "سبک جدید" بر قالببندی به سبک -%اپراترور
ترجمح داده مغشود .با این حال ،با انتشار پایتون 3.6حتغ روشغ بهتر بررای قالرببنردی
متنهای شما وجود دارد .در بخی بعدی ،در مورد آن همه چمز را خواهم گفت.
60ترفندهای پایتون
این سمنتاس جدید قالببندی ،قدرتمنرد اسرت .چرون مرغتوانمرد هرر نرو عبرارات
دلخواه پایتون را در رشتهی خود جاسازی پنمد ،حتغ مغتوانمد محاسبات درونخطغ را
با آن انزام دهمد ،مانند مثال زیر.
>>> 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است را داریم.
هنگامغ په تابع را تززیه مغپنمم و آنچه را پره در پشرت صرحنه اتفراق مرغافترد را
بررسغ مغپنمم ،مشاهده مغپنمم په f-stringsدر این تابع به چمرزی شربمه بره پرد زیرر
تبدیل مغشود.
رشتههای نوشتاری جدید قالببندی شده در پایتون شبمه به الگوهای نوشرتاری جراوا
اساریپت در ES2015است .فار مغپنم ویژگغ پامالد خوبغ برای زبان هسرتند و قربالد
1 Opcode
62ترفندهای پایتون
از آنها در پارهای روزانه پایتون 3اسرتفاده پرردهام .در مسرتندات رسرمغ پرایتون مرغ
توانمد مطالب بمشتری در مورد الحاق رشتههای قالب بندی شده یاد بگمرید.
.4-5-2الگوهای رشتهها
یک روش دیگر برای قالببندی رشته در پایتون ،استفاده از الگوی رشتهها (یا قالب
رشتهها) 1است .این ماانمزمغ سادهتر و با صرف هزینه پمتر است ،اما در برخرغ مروارد
ممان است دقمقا همان چمزی باشد په به دنبال آن هستمد.
اجازه دهمد تا یک مثال ساده را بررسغ پنمم.
نحوه پار این روش عالغ است ،به عقمده مرن ،بهتررین مرورد اسرتفاده بررای الگروی
رشتهها زمانغ است په رشرتههرای ورودی تولمرد شرده توسرط پراربران را قالرب بنردی
مغپنمد .زیرا به دلمل پاهی مخراطرات ورودی هرای ارسرالغ از طررف پراربران ،ایرن
روش انتخاب امن تری است.
قالببندی بسمار پمچمده رشتهها ممان است آسرمبپرذیریهرای امنمترغ را در برنامره
شما ایزاد پند .برای مثال ،این اماان برای قالب بندی نادرست رشتهها وجرود دارد پره
به متغمرهای دلخواه در برنامه شما دسترسغ پمدا پنند.
این بدین معناست په اگر پاربر مخرب بتواند رشته قالب را تهمه پند ،پرس احتمراالد
مغتواند به پلمدهای مخفغ و سایر اطالعات حساس نمز نفوذ پند! در اینزا ا برات سراده
ای درباره نحوه استفاده از این حمله وجود دارد.
# 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ترفندهای پایتون
ذن پایتون.6-2
یک دید مشترک، تا جایغ په پایتون ادامه دارد،مغدانمم آنچه په به شرح زیر است
اما واقعا همک روشرغ در مرورد اجررای ذن پرایتون وجرود.بمن برنامهنویسان پایتون است
و فارر مرغپرنم سرخنان ترمم، من از بازبمنغ ذن پایتون در این سال ها سود بردهام.ندارد
اممدوارم ذن پایتون بتواند هممن پار را برای.مرا به برنامهنویس بهتری تبدیل پرد1 پمترز
.شما انزام دهد
برای مشاهده ذن پایتون الزم است وارد مفسر پایتون شوید و چنمن دستوری را اجررا
.پنمد
1 Tim Peters
66ترفندهای پایتون
به این جمرالت ذن 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ترفندهای پایتون
)'>>> yell('hello
'!'HELLO
این خط تابع را فراخوانغ نمغپند .این خط ،شغء تابعغ به نام yellرا مغگمرد و آن
را در شغء barkپه به آن اشاره مغپند ،قرار مغدهد .حال مغتوانمد با فراخوانغ ،bark
همان تابع اصلغ yellرا نمز اجرا پنمد.
)'>>> bark('woof
'!'WOOF
اشمای تابع و نامهای آنها دو مسأله جداگانه هستند .پمغ عممقتر بنگریم ،شرما مرغ
توانمد نام اصلغ تابع ( )yellرا حذف پنمد .چون نام دیگرر ( )barkهنروز بره ترابع اصرلغ
اشاره مغپند ،سپس مغتوانمد این تابع را از طریق دستور زیر فراخوانغ پنمد.
)'?>>> yell('hello
"NameError: "name 'yell' is not defined
)'>>> bark('hey
'!'HEY
فصل سوم :توابع تأثیرگذار 73
به هر حال ،پایتون با اهداف اشاالزدایغ و عمبیابغ ،شناسه نام ترابع را بره ترابع در
زمان اجرا ،متصل مغپند .با تابع جادویغ __ __nameمرغتوانمرد بره ایرن شناسرهی نرام
داخلغ دسترسغ پمدا پنمد.
__>>> bark.__name
''yell
حال ،با ایناه مقدار __ __nameتابع هنوز " "yellاست ،اما بر نحوه دسترسرغ شرما
به شغء تابع ا ر نمغگذارد .شناسه نام تابع صررفاد بررای پمرک بره اشراالزدایرغ اسرت.
بنابراین در این قسمت متوجه شدیم متغمری په به نام تابع اشاره مغپند و خود ترابع ،دو
نگرانغ متفاوت هستند.
دسترسغ به اشمای تابع ذخمره شده در لمست ،مانند دسترسغ به هر نو شغء دیگرری
در زبان پایتون است.
حتغ مغتوانمد شغء تابع ذخمره شده در لمست را به صورت مستقمم فراخوانغ پنمد.
)'>>> funcs[0]('heyho
'!'HEYHO
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زیر ممان است عملارد بهتری داشته باشد( .زیرا بره جرای حرروف برزرگ،
حروف پوچک یک متن را نمایی مغدهد)
)>>> greet(whisper
''hi, i am a python program...
توانایغ انتقال اشماء تابع به صورت آرگومانهایغ به توابع دیگرر ،ویژگرغ قدرتمنردی
است .این پار به شما اجازه مغدهد تا انتزا را پنار بگذارید و بر رفترار در برنامرههرای
خود تمرپز پنمد .در این مثرال ،ترابع greetیاسران مرغمانرد ،امرا مرغتوانمرد برا انتقرال
رفتارهای مختلف ،بر خروجغ آن ا ر بگذارید.
توابعغ په مغتوانند سایر توابع را بعنوان آرگومانها بپذیرند ،توابع مرتبه براالتر 1نمرز
ناممده مغشوند .آنها الزامغ برای سبک برنامهنویسغ تابعغ 2هستند.
مثال پالسمک برای توابع مرتبه باالتر در پایتون ،ترابع داخلرغ mapدر زبران پرایتون
است .این شغء تابع و تاررار شرونده 3را مرغگمررد و بعرد ترابع را روی هرر عنصرری در
تارارشونده فراخوانغ مغپند و همانطور په پمی مغرود ،نتایج را بدست مغ آورد.
در اینزا نحوه قالببندی توالغ احوالپرسغ را بصورت یازا با نگاشتغ از ترابع bark
نوشته ایم په نتمزه در نهایت در یک لمست ذخمره مغشود.
همانطور په دیدید نگاشت در پل لمست انزام شده و تابع barkبه هر عنصر لمسرت
اعمال شد .در نتمزه ،حاال یک شغ لمست جدید با رشتههای اصالح شده احوالپرسرغ را
داریم.
هر بار په تابع 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
البته ،مغتوانمد ادامه دهمد و با استفاده از تابع برگشت یافته ،مسرتقمما یرا برا تخصرمص
آن به متغمر ،فراخوانغ را انزام دهمد.
اجازه دهمد چند لحظه در این مورد تأمل پنمم .این بدان معناست پره توابرع نره تنهرا
مغتوانند رفتارها را از طریق ورودیها بپذیرند ،بلاه مغتوانند رفتارها را نمز برگرداننرد.
به نظرتان چطور است؟
همانطور په مغدانمد ،گاهغ درک بعضغ از مفاهمم برنامهنویسغ په تودرترو هسرتند
پمغ دشوار است .قصد دارم قبل از ادامه نوشتن پتاب ،استراحت پوتاهغ پنم و دمرغ
به یک فنزان قهوه بزنم ( .پمشنهاد مغپنم شما هم این پار را انزام دهمد)
78ترفندهای پایتون
حال به توابع داخلغ whisperو yellخوب نگاه پنمرد .دقرت پردهایرد پره چگونره
آنها دیگر پارامتر textرا به عنوان ورودی ندارند؟ اما هنروز مرغتواننرد بره پرارامتر text
تعریف شده در تابع والد دسترسغ پمدا پنند .در حقمقت ،اینطور به نظر مغرسد په آنهرا
مقدار آرگومانهای والد را بت پرده و به یاد مغآورند.
فصل سوم :توابع تأثیرگذار 79
توابعغ په این پار را انزام مغدهند ،بستارهای معنرایغ 1ناممرده مرغشروند (یرا بطرور
خالصه ،فقط بستارهرا 2ناممرده مرغشروند) .بسرتار ،مقرادیر را از ترابع والرد خرود بره یراد
مغآورد ،حتغ اگر جریان اجرای برنامه دیگر در این تابع نباشد .به زبان ساده ،این بردان
معناست په توابع نه تنها مغتوانند رفتارها را برگردانند بلاه مغتوانند از قبل ،آن رفتارها
را پماربندی پنند .برای توضمح این ایده ،مثالغ مغزنم.
)>>> plus_3(4
7
)>>> plus_5(4
9
در این مثال make_adder ،به عنوان دیزاین پترن 3 factoryبرای ایزاد و پماربندی
تابع addعمل مغپند .دقت پنمرد پره چگونره ترابع addمغتوانرد بره مقردار nاز قبرل
مقداردهغ شده ،دسترسغ پمدا پند .بهترر اسرت زمرانغ را بررای درک عممرق ایرن مثرال
دهمد. اختصا
اگر شغ قابل فراخوانغ باشد به این معنغ است په مغتوانمد از سمنتاس پرانترز براز و
بسته برای آن استفاده پنمد و در صورت نماز مقادیری را به شغ پاس دهمد .این قابلمت با
استفاده از متد جادویغ __ __callانزام مغشود.
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
return self.n + x
در پشت صحنه ،زمانغ په یک شغ را همانند تابع فراخوانغ مغپنمد ،پرایتون ترالش
مغپند تا تابع __ __callدرون شغ را فراخوانغ پند.
البته همه اشماء قابل فراخوانغ نمستند .به هممن دلمل یک تابع callableداخلغ وجرود
دارد تا بررسغ پند په آیا شغء قابل فراخوانغ است یا خمر.
)>>> callable(plus_3
True
)>>> callable(yell
True
)'>>> callable('hello
False
توابع ،به عنوان شهروندان درجه اول به شما اجرازه مرغدهنرد ترا انترزا را پنرار
بگذارید و بر رفتارهای برنامه خود توجه پنمد.
توابع را مغتوان به صورت تو در تو ایزاد پرد و آنها مغتوانند برخرغ حراالت
تابع والد را بت پرده و حمل پننرد .تروابعغ پره ایرن پرار را انزرام مرغدهنرد،
بستارها ناممده مغشوند.
اشماء را نمز مغتوان قابل فراخوانغ پرد .در ارلب موارد ،این پار به شرما اجرازه
مغدهد تا با آنها مانند توابع رفتار پنمد.
مغتوانمد همان تابع جمع را با پلمدواژه defتعریف پنمد ،اما پمغ طوالنغتر خواهد
بود.
حال ممان است پنزااو باشمد په "چرا در مورد المبداها این همره جنزرال وجرود
دارد؟ اگر آنها نسخه پمغ مختصر و مفمدتری از تعریف توابع با defهسرتند ،پرس چره
مشالغ وجود دارد؟"
82ترفندهای پایتون
به مثال زیر نگاهغ بمندازید و در حمن انزام این پار ،عبارات بمران ترابع 1را در ذهرن
داشته باشمد.
بسمار خب ،اینزا چه خبر است؟ فقط از المبدا برای تعریف ترابع جمرع درون خطرغ
استفاده پردم و بعد بالفاصله آن را با آرگومانهای 5و 3فراخوانغ پردم.
به لحاظ مفهومغ ،عبارت المبدا lambda x, y: x + yهمانند اعالم تابع با defاست،
اما فقط درون خطغ نوشته شده است .تفاوت اصلغ در اینزا ایرن اسرت پره الزم نمسرت
نامغ را برای تابع انتخاب پنم .فقط گفتم په مغخواهم محاسبه سریعغ انزام دهم و بعد
بالفاصله آن را با فراخوانغ عبارت المبدا مانند تابع های عادی اجرا پردم.
پمی از پرداختن به مورد بعدی ،ممان است بخواهمد پمغ در مورد مثرال قبرل فارر
پنمد تا واقعا مفهوم آن را دریابمد .هنوز به یاد دارم په این پرار بررای مرن مردتغ طرول
پشمد ،چون نمغتوانستم ذهنم را روی آن متمرپز پنم .بنابراین نگران صرف چند دقمقه
در مفسر پایتون در این باره نباشمد .این پار ارزشی را دارد.
تفاوت نحوی دیگری بمن تعاریف المبدا و تابع عادی وجرود دارد .توابرع المبردا بره
عبارت محاسباتغ واحدی محدود مغشوند .این بدان معنغ است په تابع المبدا نمغتواند
از جمالت 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مرترب
مغپنمم .در این حالت ،تابع المبدا سریعا روشغ برای اصالح و مرتبسرازی فرراهم مرغ
پند .در اینزا نمونه دیگری از مرتبسازی است په مغتوانمد در مورد آن فار پنمد.
اممدوارم بتوانمد نحوه استفاده از المبدا بررای انعطرافپرذیری بمشرتر در محاسربات را
ببمنمد .آیا مغخواهمد توالغ را با پلمدی په بصورت دلخواه محاسبه شرده مرتربسرازی
پنمد؟ مشالغ نمست .حاال نحوه انزام این پار را مغدانمد.
ناته جالب دیگری درباره المبداها وجود دارد :دقمقا مانند توابع تو در ترو ،المبرداها
نمز مغتوانند همانند بستارهای معنایغ عمل پنند.
بستار معنایغ 1چمست؟ این فقط نامغ زیبا برای ترابعغ اسرت پره مقرادیر را از حروزه
محصور شده فراخوانغ مغپند ،حتغ وقتغ په جریان برنامه دیگر در این حروزه نباشرد.
در اینزا مثال (نسبتا دانشگاهغ) برای توضمح این ایده ارائه شده است.
)>>> plus_3(4
7
)>>> plus_5(4
9
در مثال فوق ،المبدای x+nمغتوانرد بره مقردار nدسترسرغ پمردا پنرد اگرر در ترابع
( make_adderحوزه محصور) تعریف شده باشد.
گاهغ ،استفاده از تابع المبدا بزای تابع تو در توی اعالم شرده برا پلمردواژه defمرغ
تواند هدف برنامهنویس را برا وضروح بمشرتری بمران پنرد .صرادقانه بگرویم ،ایرن اتفراق
معمولغ نمست ،حداقل در سبک پدی په مایلم بنویسم ،اینطور نمست .پس پمغ بمشرتر
در مورد آن صحبت پنمم.
# Harmful:
>>> class Car:
)'!rev = lambda self: print('Wroom
)'!crash = lambda self: print('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ها پار پمچمدهای پردهاید و از این پار لذت
مغبرید ،پمشنهاد مغپنم په این پار پمچمده را به صورت یک تابع عادی با خوانایغ پد
بمشتری انزام دهمد .صرفهجرویغ در فشرردن پلمردها در درازمردت اهممترغ نردارد ،امرا
همااران شما (و خود شما در آینده) پد تممز و خوانرا را بمشرتر از یرک جرادوگر مراهر
مغتوانمد تغممر دهمد.
هممشه از خود بپرسمد :آیا استفاده از توابع عادی با نام گذاری صحمح ،خوانرایغ
بمشتری دارد؟ اگر پاس بله است ،از المبدا استفاده نانمد.
1
.3-3قدرت اعجاب انگیز دکوریتورها
تصور پنمد 30تابع با منطق های پمچمرده پسرب و پرار در برنامره خرود نوشرته ایرد.
در یک صبح شنبه بارانغ ،رئمس پنار ممز شما مغآید و مغگوید:
“صبح شرنبه بخمرر! آن گزارشرهای TPSرا بره خراطر داری؟ نمراز دارم ترا داده هرای
ورودی و خروجغ در هر مرحله تولمد گزارش را بره صرورت پامرل برت پنمرد .بررای
اهداف حسابرسغ با شرپت XYZایرن اطالعرات بایرد آمراده شروند .بره آنهرا گفرتم پره
مغتوانمم این پار را تا روز دوشنبه ارسال پنمم”.
این درخواست فشار خون شما را افزایی خواهد داد یا باعرث آرامری شرما خواهرد
شد .بستگغ به این دارد په آیرا شرما از دپوریتورهرای پرایتون بره درک عممقرغ دسرت
یافته اید یا خمر .بدون دپوریتورها ،ممان است سه روز آینده را صرف اصالح هر یرک
از این 30تابع پنمد و با فراخوانغ های دستغ بت وقایع و گزارشگمری آنهرا را درهرم
بریزید .لحظات سرگرم پننده ای است ،اینطور نمست؟ با این حال ،اگرر دپوریتورهرای
پایتون را بشناسمد ،با آرامی به رئمس خود لبخند مغزنمد و مغگویمد:
“جمم نگران نباش ،امروز تا ساعت 2بعد از ظهر این پار را انزام خواهم داد”.
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
)(>>> 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ترابع را بالفاصرله در زمران تعریرف
تزئمن مغپند .این سبب دسترسغ دشوار به نسخه اصرلغ ترزئمن نشرده مغشرود .بنرابراین
ممان است برای حفظ اماان فراخوانغ تابع تزئمن نشده ،گزینره هرای مختلفرغ را بررای
تزئمن دستغ انتخاب پنمد( .در ادامه در رابطه با این ویژگغ توضمحات بمشتری خرواهمم
داد(
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ترفندهای پایتون
>>> 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
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را در نظر بگمرید.
def proxy(func):
def wrapper(*args, **kwargs):
)return func(*args, **kwargs
return wrapper
سپس تابع wrapperآرگومان های جمع آوری شده توسط عملاررد * و ** را
بازگشایغ مغپند 1و آن را به تابع ورودی ارسال مغپند.
اممدوارم ایده اصلغ مسئله را متوجه شده باشمد هرچند مغدانم پمغ نماز به تمرین برا
پدها دارید تا ایده اصلغ را به خوبغ متوجه شوید.
اجازه دهمد روش گذاشته شده با دپوریتور پراپسغ را در مثالغ عملغ تر بسط دهمم.
در اینزا دپوریتور traceرا خواهمم سراخت پره آرگومران هرای ورودی ترابع و نترایج
حاصل از آن را در طغ زمان اجرا بت مغپند.
def trace(func):
def wrapper(*args, **kwargs):
' )(}__print(f'TRACE: calling {func.__name
)'}f'with {args}, {kwargs
return original_result
return wrapper
تزئمن تابع با تابع traceو بعد فراخروانغ آن ،آرگومران هرای منتقرل شرده بره ترابع و
مقدار برگشتغ آن را چاپ خواهد پرد.
این پار گاهغ اوقات به عنوان یک پمک عالغ برای خطایابغ محسوب مغشود.
@trace
def say(name, line):
'}return f'{name}: {line
در رابطه با خطایرابغ صرحبت پرردیم ،مرواردی وجرود دارنرد پره هنگرام خطایرابغ
دپوریتورها باید به خاطر داشته باشمد.
def greet():
""""""Return a friendly greeting.
'!return 'Hello
)decorated_greet = uppercase(greet
__>>> 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
@uppercase
def greet():
""""""Return a friendly greeting.
'!return 'Hello
__>>> greet.__name
''greet
__>>> greet.__doc
''Return a friendly greeting.
فصل سوم :توابع تأثیرگذار 99
به عنوان بهترین روش ،توصمه مغپنم په در تمام دپوریتورهایغ په مغنویسرمد ،از
دستور functools.wrapsاستفاده پنمد .این پار زیاد طول نمغپشد ولغ باعث مغشود
شما و بقمه از دردسرهای خطایابغ در دپورتمورها در امان بمانمد.
تبریک مغگویم ،شما به انتهای این مطلب در رابطه با دپوریتورهای پایتون رسمدید
و احتماال درباره دپوریتورهای موجود در پایتون چمزهای زیادی آموختره ایرد .پارتران
عالغ بود!
زمانغ همراه با پایتونمستغ باهوش په اصطالح او " "arghو "" kwarghبرود ،برنامره
نویسرغ دو نفررره انزرام مررغدادم .او هرر بررار تعریرف ترابع را بررا پارامترهرای اختمرراری یررا
پلمدواژهای تایپ مغپرد .ما در پنار یادیگر به بمنی دیگری از پایتون دست یافتمم.
پارامترهررای *argsو **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
این روش جالب مغتواند در ارث بری ،ارتباط جالبغ بمن پالس فرزند و پالس پدر
برقرار پند .شما مغتوانمد از این ترفند بررای اضرافه پرردن یرک رفترار بره پرالس پردر
استفاده پنمد بدون ایناه امضای مترد سرازنده پرالس پردر را تغممرر دهمرد .ایرن روشرغ
پاربردی برای زمانمست په ممان است از یک 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به سادگغ تمام آرگومانها را بره پرالس والرد
خود منتقل مغپند .این بدان معناست په اگر متد سازنده پالس والد تغممر پند ،پالس
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
همانطور په مشاهده پردید ،با ایرن روش توانسرتمم پارامترهرای اختمراری را بره ترابع
دپوریتور منتقل پنمم .گاهغ از این روش به منظور رعایت اصل 1 DRYاسرتفاده خواهرد
شد.
همانطور په مشاهده مغپنمد ،این تابع سه آرگومان ( y ،xو )zرا مغگمرد و آنهرا را
به روشغ زیبا چاپ مغپند .ممان است از این تابع بررای چراپ بردارهرای 3بعردی در
برنامه خود استفاده پنمم.
حال بسته به نو ساختار دادهای په برای نمرایی بردارهرای سره بعردی انتخراب مرغ
پنمم ،چاپ آنها با تابع print_vectorممان است پمغ ناخوشایند باشد .بررای مثرال،
اگر بردارهای خود را به صورت تاپلها یا لمستها مقدار دهغ پنمم ،هنگام چاپ آنها،
باید به طور صریح ،اندیس هر مولفه را مشخص پنمم.
><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پارساز اسرت .برا
استفاده از عملگر * روی مولد ،تمام عناصر مولد را اجرا مغپند و آنها را به ترابع منتقرل
مغپند.
عالوه بر عملگر * برای باز پردن توالغهایغ ماننرد تاپرلهرا ،لمسرتهرا و مولردها در
داخل آرگومانهای موقعمتغ ،عملگر ** برای باز پردن آرگومرانهرای پلمردواژهای از
دیاشنری وجود دارد .تصور پنمد وپتور ما بصورت شغء دیاشنری زیر تعریرف شرده
است.
مررغترروانمم بررا اسررتفاده از عملگررر ** برررای برراز پررردن ،ایررن دیاشررنری را برره تررابع
print_vectorمنتقل پنمم.
)>>> print_vector(**dict_vec
> < 1, 0, 1
چون دیاشنریها بدون ترتمب هستند ،پس مقادیر دیاشنری و آرگومانهای تابع برر
پایه پلمدهای دیاشنری تطبمق داده مغشوند .آرگومان xمقردار مررتبط برا پلمرد ' 'xدر
دیاشنری را دریافت مغپند.
اگر بخواهمد از عملگر تک ستاره (*) برای باز پردن بستهبنردی دیاشرنری اسرتفاده
پنمد ،پلمدها به صورت ترتمب تصادفغ ،به این تابع منتقل خواهند شد.
)>>> print_vector(*dict_vec
> < y, x, z
ویژگغ باز پردن بستهبندی آرگومان تابع در پرایتون ،انعطراف پرذیری زیرادی را در
اختمار شما قرار مغدهد .ارلب این بدان معنغ است په شما نمازی به پمادهسرازی پرالس
برای نو دادههای مورد نماز با برنامه خود ندارید .در نتمزه ،استفاده از ساختار دادههرای
درونغ ساده مانند تاپلها یا لمستها پافغ خواهد بود و بره پراهی پمچمردگغ پرد شرما
پمک خواهد پرد.
پایتون برگشت ضمنغ 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-4مقایسه شیء ها "is" :در مقابل "=="
وقتغ بچه بودم ،همسایههای ما دو گربه دوقلو داشتند .آنها ظاهرا مشابه بودنرد ،خرز
زرالغ و چشمهای سبز تمرزبمن مشرابهغ داشرتند .اگرر برخرغ از ویژگرغهرای شخصرمتغ
گربهها پنار گذاشته شوند ،نمغتوانستمد با نگاه پردن به آنها تفاوتشان را بگویمد .البته،
آنها دو گربه متفاوت بودند ،دو موجود جداگانه ،با وجود ایناه دقمقرا یاسران بره نظرر
مغرسمدند.
این پار در من تفاوت در معنایغ بمن برابرر و مشرابه را القراء پررد و ایرن تفراوت در
درک نحوه رفتار عملگرهای مقایسه isو == در پایتون حائز اهممت است.
عملگر == با بررسغ برابری مقایسه مغپند :اگر این گربهها اشماء پایتون بودنرد و آن
ها را با عملگر == مقایسه مغپردیم ،در پاس "هر دو گربه برابر هستند"را بدست مرغ-
آوردیم.
با این حال ،عملگر ،isهویت ها را با هم مقایسه مغپند :اگرر گربرههرای خرود را برا
عملگر isمقایسه مغپردیم ،در پاسر "ایرن دو گربره متفراوت هسرتند" را بدسرت مرغ
آوردیم.
قبل از ایناه گرفتار این قماس پالفه پننده گربه شروم ،اجرازه دهمرد یرک تاره پرد
پایتون واقعغ را بررسغ پنمم.
ابتدا شغء جدید لمست را ایزاد مغپنمم و آن نامگذاری مغپنمم ،و بعد متغمر دیگرر
( )bپه به همان شغء لمست اشاره دارد را تعریف مغپنمم.
اجازه دهمد این دو متغمر را بررسغ پنمم .مغتوانمم مشاهده پنمم په آنها بره لمسرت
های در ظاهر یاسان اشاره مغپنند.
>>> 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ترفندهای پایتون
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
بطور پمیفرض ،تمام آن چمزی په بدست مرغآوریرد ،رشرته حراوی نرام پرالس و
شناسه نمونه شغء (په آدرس حافظه شغء در CPythonاست) مغباشد .این بهتر از همک
است ،اما چندان مفمد نمست.
ممان است پمرغ روی ایرن شرغء پرار پررده باشرمد و برا اسرتفاده از پرینرت یرک
خروجغ برای شغء خود درنظر بگمرید یا حتغ یک متد اختصاصغ )( to_stringنوشرته
باشمد په این پار را مغپند.
به جای نوشرتن یرک مترد )( 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را چاپ مغپنمد ،نتمزهای متفاوت و پمغ بهبودیافته بدست
خواهمد آورد.
بررسغ شغء 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نمرز از
قوانمن مشابهغ پمروی مغپند و همک چمز جادویغ یا سری در مورد آن وجود ندارد.
از استفاده از متدهای داندر پایتون نترسمد ،اینها به شما پمک مغپنند.
داستان تبدیل رشته ما به اینزا ختم نمغشود .آیا نحوه بررسغ 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
حال ،وقتغ از طریق نمونههای قبلغ پد را اجرا مغپنمد ،مغفهممرد پره پردام روش
نتمزه تبدیل رشته را در هر مورد پنترل مغپند.
این آزمایی تایمرد مرغپنرد پره بررسرغ یرک شرغء در مفسرر پرایتون صررفا نتمزره
__ __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برای پنترل تبدیل رشته استفاده مغپند.
نتمزه تابع __ __strشغء تاری در پایتون باید خوانا باشد .ایرن بره معنرای برگشرت
نمایی متنغ دقمق برای برنامهنویسان است (چمزی په شما با آن احساس راحتغ مغپنمد
را نشان مغدهد ).بنابراین ،هنگام فراخوانغ با متد )( strدر مورد شغء تاری ،چمزی شبمه
به فرمت تاری استاندارد را بدست مغآوریم.
)>>> str(today
''2017-02-02
با متد داندر __ ،__reprمعتقدیم په مهمتر از همره ،نتمزره بایرد بردون ابهرام باشرد.
رشته حاصل بمشتر به عنوان پمک به اشاالزدایغ برای توسعهدهنردگان در نظرر گرفتره
شده است و به هممن خاطر باید در مورد محتویات این شغء ،تا حد اماان صریح باشرد.
به هممن دلمرل نتمزره دقمرقترری بدسرت خواهمرد آورد اگرر مترد )( reprرا بررای شرغء
فراخوانغ پنمد .این نتمزه حتغ شامل نام پامل ماژول و نام پالس نمز مغباشد.
فصل چهارم :کالسها و برنامهنویسی شیء گرا 121
)>>> repr(today
')'datetime.date(2017, 2, 2
مغتوانمم رشته برگشت داده شرده را برا __ __reprپپرغ و پمسرت پنرمم و آن را بره
عنوان پد معتبر پایتون اجررا پنرمم ترا شرغء اصرلغ تراری را دوبراره ایزراد پنرمم .ایرن
رویاردی عالغ برای نوشتن reprهای اختصاصغ است.
از طرف دیگر ،درمغیرابمم پره نوشرتن __ __reprهرای اختصاصرغ پمرغ دشروار
است .معموال فار مغپنمد ارزش این همه زحمت را نخواهد داشت و فقط پرار اضرافغ
به شما تحممل مغپند .قاعده سرانگشتغ من این است په رشتههای __ __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را به پالسهای خود
اضافه مغپرنم .در اینزرا مثرال پراملغ بررای پرایتون ،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.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
با استفاده از متدهای داندر __ __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
)'>>> 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
پاربرانتان مغتوانند بدین شموه اسرتثناءهای خرا ترری را بدسرت آورنرد ،امرا اگرر
نخواهند ،حداقل مزبور نخواهند بود برای گرفتن استثناءها با قالب در پد به دنبال خطرا
بگردند .البته این پار ممان است گاهغ یک ضدالگو در نظر گرفته شود زیرا مغتوانرد
خطاهای نامربوط برنامه را در بلند مدت از بمن ببرد و پنهان پند و اشراالزدایغ برنامره
های شما را بسمار سختتر پند.
البته ،مغتوانمد این ایده را بمشتر گسترش دهمد و به لحاظ منطقرغ اسرتثنائات خرود را
در سلسله مراتب فرعغ گروهبندی پنمد .اما مراقب باشمد ،زیادهروی پردن در این پار،
ورود پمچمدگغ رمرضروری به پدهای شما را آسان مغپند.
)new_list = list(original_list
)new_dict = dict(original_dict
)new_set = set(original_set
با این حال ،این روش برای اشماء سفارشغ مناسب نمست و از همه مهمتر ایناره ،ایرن
روش فقط پپغهای سطحغ 2را ایزاد مغپند .برای مزموعههای تغممرپذیر مانند لمسرت
ها ،دیاشنریها و setها ،یک تفاوت مهم بمن پپغ سطحغ و پپغ عممق 3وجود دارد.
پپغ سطحغ به معنای ایزاد شغء جدید مزموعه و بعد مقداردهغ آن با مراجعره بره
اشماء موجود در لمست اصلغ است .در حقمقت ،پپغ سطحغ فقط یک عمق دارد .روند
پپغبرداری تارار نمغشود و از ایرن رو ،پپرغ جدیردی از اشرماء درون مزموعره پپرغ
شده ،ساخته نخواهد شد.
پپغ عممق ،روند پپغ را بصورت برگشتغ 1انزام مغدهد .این بدین معنغ اسرت پره
ابتدا شغء مزموعه جدید ساخته مغشود و بعد بصورت برگشرتغ برا پپرغ اشرماء فرزنرد
موجود در مزموعه اصلغ مقداردهغ مغشود .پپغبرداری از یرک شرغء برا ایرن روش،
پل درخت شغ 2را طغ مغپند تا پلون پامالد مستقل از شغء اصلغ و همه فرزنردان آن
ایزاد پند.
مغدانم ،توضمحات پمغ طوالنغ بود .پس اجازه دهمد چند مثال را بررسغ پنرمم ترا
این تفاوت بمن پپغهای سطحغ و عممق را مشخص پنمم.
این بدان معناست په 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ا رر
نمغگذارد.
همانطور په مغبمنمد ،این پار ا ر مورد انتظار را داشت .اصالح لمست پپرغ شرده در
سطح "ظاهری" اصال مشال نبود.
با این حال ،چون فقط پپغ سطحغ لمست اصرلغ را ایزراد پرردیم ys ،هنروز حراوی
ارجا هایغ به اشماء فرزند لمست اصلغ ذخمره شده در xsاست .این فرزندان به صرورت
پامل پپغ نشده بودند.
بنابراین ،هنگامغ په یارغ از اشرماء فرزنرد را در xsاصرالح مرغپنمرد ،بازتراب ایرن
اصالحات در ysنمز خواهد بود! این اتفاق به این دلمرل اسرت پره هرر دو لمسرت ،اشرماء
فرزند مشابهغ را به اشتراک مغگذارند .این پپغ فقط یک پپغ سطحغ اسرت ،درواقرع
در مرحله پپغ پردن ،تنها یک عمق پپغ اعمال مغشود.
در مثال فوق (بره ظراهر) فقرط xsرا تغممرر دادهایرم .امرا معلروم مرغشرود پره هرر دو
زیرلمست با اندیس 1در xsو ysاصالح شدند .از طرف دیگر ،سبب وقو این امرر ایرن
بود په فقط پپغ سطحغ لمست اصلغ را ایزاد پرده بودیم.
اگر در وهله اول پپغ عممق از xsایزراد پررده برودیم ،هرر دو شرغء پرامالد مسرتقل
بودند .این تفاوت پاربردی بمن پپغهای سطحغ و عممق اشماء است.
فصل چهارم :کالسها و برنامهنویسی شیء گرا 133
حال مغدانمد په چگونه پپغهای سطحغ برخغ پالسهای مزموعه داخلغ پرایتون
را ایزاد پنمد و تفاوت بمن پپغبرداری سطحغ و عممق را نمز مغدانمد .سؤاالتغ په حاال
مغخواهمم به آنها پاس دهمم عبارتند از:
چگونه مغتوانمد پپغهای عممق را در مزموعههای داخلغ پایتون ایزاد پنمد؟
چگونه مغتوانمد پپغهای سطحغ و عممق اشماء دلخرواه ،از جملره پرالسهرای
سفارشغ را ایزاد پنمد؟
پاس این سؤاالت در ماژول پپغ در پتابخانه استاندارد پایتون قرار دارد .این ماژول
رابط سادهای را برای ایزاد پپغهای سطحغ و عممق اشماء دلخواه پایتون فراهم مغپند.
>>> xs
]][[1, 2, 3], [4, 5, 6], [7, 8, 9
>>> zs
]][[1, 2, 3], [4, 5, 6], [7, 8, 9
با این حال ،اگر در یاغ از اشماء فرزند در شغ اصلغ ( )xsاصرالحاتغ انزرام دهمرد،
خواهمد دید په این اصالحات روی پپغ عممق ( )zsا ری نخواهد داشت.
134ترفندهای پایتون
این بار هر دو شرغء ،اصرل و پپرغ ،پرامالد مسرتقل از یاردیگر هسرتند .پپرغ xsبره
صورت برگشتغ پلونسازی شد ،په شامل همه اشماء فرزند آن مغباشد.
ممان است بخواهمد مدتغ با مفسر پایتون بنشمنمد و این مثالها را اجررا پنمرد .وقترغ
مستقمما مثالها را اجرا مغپنمد ،فار پردن در مورد اشماء پپغبرداری شرده راحرت ترر
است.
در هر حال ،مغتوانمد با استفاده از یک تابع دیگر در ماژول پپغ ،پپغهرای سرطحغ
نمز ایزاد پنمد .تابع )( copy.copyپپغهای سطحغ اشماء را ایزاد مغپند.
اگر الزم است ارتباط واضحغ بمن اشماء برقرار پنمرد و در جرایغ از پرد خرود یرک
پپغ سطحغ ایزاد پنمد ،این پار مفمد خواهد برود .اسرتفاده از ترابع )( copy.copyایرن
اماان را به شما مغدهد تا این واقعمت را درک پنمد .برای مزموعرههرای داخلرغ ،ایرن
پار بمشتر پایتونمک به نظر مغرسد تا صرفا از توابع dict ،listو setپایتون بررای ایزراد
پپغهای سطحغ استفاده پنمد.
یابار دیگر ،بهترین روش برای درک نحوه استفاده از اینها ،انزام آزمرایی اسرت.
مغخواهم این آزمایی را بر پایه مثال قبلغ پپغبرداری از لمست انزام دهم .اجازه دهمد
با تعریف پالس نقطه دوبعدی ساده شرو پنمم.
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ایزاد مرغپنرمم و
بعد (بصورت سطحغ) آن را پپغ مغپنمم.
اگر محتویات شغء 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
از طرف دیگر ،ابتدا مغخواهمم پپغ سطحغ نمونه مستطمل را ایزاد پنمم.
در صورت بررسغ مستطمل اصلغ و پپغ آن ،خواهمد دید په نادیده گررفتن ترابع ()
__ __ reprچقدر مو ر است و ایناه روند پپغ سطحغ طبق انتظار ما پار مغپند.
>>> rect
))Rectangle(Point(0, 1), Point(5, 6
>>> srect
))Rectangle(Point(0, 1), Point(5, 6
>>> rect is srect
False
به یاد داشته باشمد په چگونه نمونه قبلغ لمست تفاوت بمن پپغهای عممق و سرطحغ
را نشان داد؟ در اینزا مغخواهم از رویاردی مشابه استفاده پنم .یک شغء را از قسمت
فصل چهارم :کالسها و برنامهنویسی شیء گرا 137
پایمن در سلسلهمراتب شغء اصرالح خرواهم پررد و بعرد بازتراب ایرن تغممرر را در پپرغ
(سطحغ) نمز مشاهده خواهمد پرد.
اممدوارم این توابع طوری په انتظار آن را داشتمد ،رفتار پنند .در مرحلره بعرد ،پپرغ
عممق مستطمل اصلغ را ایزاد خواهمم پرد .پرس اصرالحات دیگرری را اعمرال پررده و
خواهمد دید په پدام اشماء تحت تأ مر قرار مغگمرند.
بفرمائمد! این بار پپرغ عممرق ( )drectپرامالد مسرتقل از پپرغ اصرلغ ( )rectو پپرغ
سطحغ ( )srectاست.
در اینزا قسمت زیادی از مبحث را پوشی دادهایم ،اما هنوز ناتههای ریزتری بررای
پپغبرداری از اشماء وجود دارند.
برای پرداخت بمشتر این موضو ،ممان اسرت بخواهمرد در مرورد مسرتندات مراژول
پپغ و احتماال پد منبع ماژول بمشتر مطالعه پنمد .برای مثال ،اشماء مغتواننرد برا تعریرف
)(__ __copyو )(__ __deepcopyروی آنها ،نحوه پپغبرداری را پنترل توابع خا
پنند.
138ترفندهای پایتون
برای ایناه این پد تا حد اماان پایدار و مورد پسند برنامهنویسان باشد ،بایرد مطمرئن
شویم په:
نمونهسازی پالس پایه ناممان باشد.
فرامرروش پررردن پمررادهسررازی مترردهای interfaceدر یاررغ از زیرررپالسهررا در
سریعترین زمان ممان خطایغ را آشاار پند.
حال ،چرا مغخرواهمم از مراژول abcپرایتون بررای حرل ایرن مسرئله اسرتفاده پنرمم؟
طراحغ فوق در سمستمهای پمچمده نسبتا شایع است .برای اجبار بره پمادهسرازی متردهای
پالس پایه در زیرپالسها ،معموال از روش پایتونماغ بدین صورت استفاده مغشود.
class Base:
def foo(self):
)(raise NotImplementedError
def bar(self):
)(raise NotImplementedError
class Concrete(Base):
def foo(self):
'return 'foo() called
بنابراین ،از اقدامات اولمره خرود بررای حرل ایرن مسرئله بره چره نتمزرهای مرغرسرمم؟
فراخوانغ متدهای پالس پایه به درستغ خطایغ را تولمد مغپند.
)(>>> b = Base
)(>>> b.foo
NotImplementedError
140ترفندهای پایتون
)(>>> c = Concrete
)(>>> c.foo
''foo() called
)(>>> c.bar
NotImplementedError
این پماده سازی اولمه مناسب است ،اما هنوز پامل نمست .در آن نقاط ضرعفغ وجرود
دارند ولغ مغتوانمم:
پالس پایه را بدون ایزاد خطا بدرستغ نمونهسازی پنمم.
زیرپالسهررای رمرپرراملغ را پمادهسررازی پن رمم و تررا زمررانغ پرره متررد )( barرا
فراخوانغ نانمم ،نمونهسازی معمول همک خطایغ ایزاد نخواهد پرد.
با ماژول abcپایتون افزوده شرده در پرایتون ،2.6مرغتروانمم عملاررد بهترری داشرته
باشمم و بقمه مسائل را حل پنمم .در پماده سازی زیر ،روش پماده سازی را ارتقا مرغدهمم
و از پالسهای انتزاعغ پایه استفاده خواهمم پرد.
class Base(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
@abstractmethod
def bar(self):
pass
class Concrete(Base):
def foo(self):
pass
فصل چهارم :کالسها و برنامهنویسی شیء گرا 141
این پار هنوز طبق پمیبمنغ رفتار مغپند و سلسلهمراتب پالس را به صورت صحمح
ایزاد مغپند.
با این حال ،در این پد مزیرت دیگرری را مشراهده مرغپنمم .هرر وقرت پمرادهسرازی
متدهای انتزاعغ را فرامروش پنرمم ،زیرپالسهرای پایره اسرتثنا TypeErrorرا در زمران
نمونهسازی ایزاد مغپند .استثناء مطرح شده به ما مرغگویرد پردام مترد یرا متردها را از
دست دادهایم.
)(>>> c = Concrete
TypeError:
\ " "Can't instantiate abstract class Concrete
""with abstract methods bar
.6-4آشنایی با namedtupleها
پایتون با نو اختصاصغ پانتمنر " "namedtupleهمراه است په بره نظرر نمرغرسرد
چندان سزاوار تامل باشد .این یاغ از آن ویژگغهای شگفتانگمز در پایتون است په با
دید باز نمز قابل مشاهده نمست.
استفاده از namedtupleها مغتوانرد جرایگزین بسرمار خروبغ بررای تعریرف دسرتغ
پالس باشد و ویژگغهای جالب دیگری نمز دارند په در این قسمت قصد دارم شرما را
با آنها آشنا پنم.
حال namedtuple ،چمست و چه چمزی آن را خا ترر مرغپنرد؟ روشرغ مناسرب
برای فار پردن در مورد namedtupleهرا ،مشراهده آنهرا بصرورت افزونرهای از نرو
دادههای tupleاست.
Tupleها در پایتون ،ساختار داده سرادهای بررای گرروهبنردی اشرماء دلخرواه هسرتند.
tupleها تغممرناپذیر هستند .یعنغ پس از ایزاد ،آنها را نمغتوان اصالح پررد .در ادامره
مثالغ مختصر در این زممنه وجود دارد.
یک ناته مهم در مورد tupleهای ساده این است په دادههایغ په در آنهرا ذخمرره
مغپنمد را فقط با استفاده از اندیسهای عدد صحمح استخراج پرد .نمغتوانمد به مقادیر
دهمد .این مغتواند بر خوانایغ پد ا ر بگذارد. ذخمره شده در tupleنامهایغ اختصا
همچنمن tuple ،همواره از ساختار موقتغ برخوردار است .پسب اطممنان از ایناه دو
تاپل از تعداد فملدهای یاسران و خصوصرمات ذخمرره شرده مشرابه برخوردارنرد ،دشروار
است .این پار ممان است باعث مشاالتغ در هنگام پار با tupleها شود.
البته ،مغتوانمد لمستغ را مستقمما با نامهای رشتهها وارد پنمد .مزیت استفاده از لمسرت
این است په اگر بخواهمد آن را به چندین خط تززیه پنمد ،این پار سادهتر خواهد بود.
حال مغتوانمد به راحتغ شغء جدید Carرا ایزاد پنمد .شغء carطروری رفترار مرغ
پند په گویغ پالس Carرا به صورت دستغ تعریف پردهاید و به آن سازندهای داده-
اید په "رنگ" و مقدار "مسافت پمموده شده بر حسب مایل" را مغپذیرد.
]>>> my_car[0
''red
)>>> tuple(my_car
)('red', 3812.4
باز پردن tupleو عملگر * برای بازپردن آرگومانهای ترابع نمرز طبرق انتظرار پرار
مغپند.
حتغ یاغ از فوایرد اسرتفاده از namedtupleهرا ایرن اسرت پره مغتوانمرد نمرایی
رشتهای زیبایغ برای اشماء اختصاصغ بدون همک زحمتغ داشته باشمد.
>>> my_car
)Car(color='red' , mileage=3812.4
146ترفندهای پایتون
مانند تاپلها namedtuple ،ها تغممرناپرذیر هسرتند .اگرر یارغ از فملردهای آنهرا را
بازنویسغ پنمد ،استثنا AttributeErrorرا دریافت خواهمد پرد.
class MyCarWithMethods(Car):
def hexcolor(self):
if self.color == 'red':
'return '#ff0000
else:
'return '#000000
با این حال ،این ممان است پمغ ناخوشرایند باشرد .شراید اگرر بخواهمرد پرالس برا
خصوصمات تغممرناپذیر داشرته باشرمد ،ارزش انزرام ایرن پرار را دارد ،همچنرمن در ایرن
شرایط ممان است خود را در وضعمت بدتری قرار دهمد.
برای مثال ،اضافه پردن فملد تغممرناپذیر جدید به دلمل نحوه سراخت namedtupleهرا
مستلزم دقت زیادی است .سادهترین روش برای ایزاد سلسله مرتبه ای از namedtupleها
استفاده از ویژگغ _fieldsدر تاپل های پایه پایتون است.
توسط پاربر ،بدین صورت نامگذاری شدند .بنابراین ادامه دهمد و در صورت نماز از آن
ها استفاده پنمد! نگران خصوصغ بودنشان نباشمد!
مغخواهم چند سناریو به شما نشان دهم پره در آن متردهای پمارغ 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
برای مثال ،فهممدم په استفاده از انوا دادههای موقت ماننرد دیاشرنریها برا فرمرت
ابت به namedtupleها به من پمک مغپند تا اهداف خود را با وضوح بمشتری بمران
پنم .ارلب وقتغ این سازماندهغ مزدد را انزام مغدهم ،برای مسئلهای په با آن مواجره
هستم ،راهحل جادویغ بهتری پمدا مغپنم.
همچنمن استفاده از namedtupleها روی تاپلها و دیاشنریهای سراختاریافته مرغ
تواند زندگغ هماراران مرن را راحرتترر پنرد ،چرون باعرث مرغشرود دادههرا پمرامرون
"خوداستنادی "1پدهای نوشته شده عمل پنند.
از طرف دیگر ،اگر آنها به من در نوشرتن پرد تممزترر و نگهداشرتپرذیرتر پمرک
نانند ،از namedtupleها به نفع خود استفاده نخواهم پررد .ماننرد سرایر تانمرکهرای
نشان داده شده در این پتاب ،گاهغ مغتوانند ناات بسمار خوبغ در این زممنه باشرند .برا
این حال ،اگر با احتماط مناسبغ از آنها استفاده پنمرد ،برغشرک namedtupleهرا مرغ
توانند پد پایتون بهتر و گویاتری ایزاد پنند.
1 Self-documenting
150ترفندهای پایتون
برای نشان دادن ناته خود از اتومبملها یا حموانات خانگغ استفاده مغپنند و تززیره آن
ها با روشهای سنتغ مشال است.
یک سگ شاد به چه چمزی نماز دارد؟ چهار پا و یک نام.
class Dog:
num_legs = 4 # <- Class variable
بسمار خب ،این نمایی شغگرا نمونهای از وضعمت سگغ په به تازگغ توضرمح داده
ام ،است .ایزاد نمونههای جدید سگ طبق انتظار پار مغپند و هر یک از آنها ،متغمرر
نمونهای تحت عنوان نام دریافت مغپنند.
در صورت استفاده از متغمرهای پالس ،انعطرافپرذیری بمشرتری وجرود دارد .بررای
مثال مغتوانمد بره متغمرر پرالس num_legsروی هرر نمونره شرغء Dogیرا بره صرورت
مستقمم در خود پالس Dogدسترسغ داشته باشمد.
بررا ایررن حررال ،در صررورت دسترسررغ برره متغمررر نمونرره از طریررق پررالس ،بررا خطررای
هسرتند AttributeErrorمواجه خواهمد شد .متغمرهای نمونه برای هر نمونه شغء خا
و وقتغ متد سازنده __ __initرا اجراء مغپند مقداردهغ مغشوند .آنها حتغ در خود
پالس نمز وجود ندارند.
این تمایز اصلغ بمن متغمرهای پالس و متغمرهای نمونه است.
152ترفندهای پایتون
>>> Dog.name
AttributeError:
"'"type object 'Dog' has no attribute 'name
اما به یاد داشته باشمد ،نمغخواهمم همه سگها دویدن روی شی پرا را آرراز پننرد.
پس فعال فقط هر نمونه سگ موجرود در جهران پوچرک خرود بره سروپر سرگ تبردیل
پردهایم چون متغمرپالس را تغممر دادهایم .و این پار بر روی همه سگها ،حتغ آنهایغ
په قبال ایزاد شدهاند ،ا ر مغگذارد.
بنابراین این پار عملارد چنردانغ نداشرت .دلمرل پرار نارردن ایرن اسرت پره تغممرر
متغمرپالس در فضای نامهای پالس ،بر همه نمونههرای پرالس ا رر مرغگرذارد .اجرازه
دهمد به تغممر متغمرپالس برگردیم و بزای دادن یک جفت پاهای اضافغ به همره ،فقرط
دهمم. به جک اختصا
حال ،این پار چه هموالهایغ خلق پرد؟ اجازه دهمد بررسغ پنمم.
فصل چهارم :کالسها و برنامهنویسی شیء گرا 153
خب ،این نسبتا خوب به نظر مغرسد (گذشته از این واقعمت په به جک بمچاره چند
پای اضافه دادیم) .اما چگونه این تغممر واقعا روی اشماء سگ ا ر گذاشته است؟
همانطور په مغبمنمد ،در اینزا مشال این است په ضمن گرفتن نتمزره مردنظر خرود
(پاهای اضافغ برای جک) ،متغمر نمونه num_legsرا برای نمونه جک معرفغ پردیم و
حاال متغمر جدید نمونه num_legsروی متغمرپالسغ بره همرمن نرام سرایه مرغانردازد و
هنگام دسترسغ به نمونه شغء ،آن را تحتالشعا قرار داده و پنهان مغپند.
class CountedObject:
num_instances = 0
def __init__(self):
self.__class__.num_instances += 1
>>> CountedObject.num_instances
0
>>> CountedObject().num_instances
1
>>> CountedObject().num_instances
2
>>> CountedObject().num_instances
3
>>> CountedObject.num_instances
3
به ایناه باید این پد را با یک حلقه اجرا پنمد ترا مطمرئن شروید متغمرر شرمارنده در
پالس افزایی مغیابد ،توجه داشته باشرمد .اگرر سرازنده را بره شررح زیرر نوشرته برودم،
متحمل اشتباهاتغ در محاسبات مغشدم.
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ترفندهای پایتون
با این وجود ،اممدوارم ایناه چرا و چگونه متغمرهای پالس ،به ررم وجود مشاالت
آنها مغتوانند در عمل ابزارهای مفمدی باشند را بتوانمد مشاهده پنمد .موفق باشمد!
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
متد اول در ،MyClassبه نام ،methodیک متد معمول در یک نمونه پالس است.
این نو متد ،متدی ساده است په معموال از آن استفاده خواهمد پرد .مشاهده مرغپنمرد
په این متد از یک پارامتر یعنغ selfاستفاده مغپند په در هنگام فراخوانغ این مترد بره
نمونه MyClassاشاره مغپند .البته ،متدهای نمونه مغتوانند بمی از یک پارامتر را قبول
پنند.
158ترفندهای پایتون
از طریق پارامتر ،selfمتدهای نمونه مغتوانند آزادانه بره ویژگغهرا و سرایر متردهای
روی همان شغ دسترسغ پمدا پنند .این پار در هنگام تغممر حالرت 1یرک شرغء قردرت
زیادی برای پنترل برنامه مغدهد.
آنها نه تنها مغتوانند حالت شغ را تغممر دهند ،بلاه متدهای نمونه مغتوانند از طریق
ویژگغ __ self.__classبه خود پالس دسترسغ پمدا پنند .ایرن بردان معنرغ اسرت پره
متدهای نمونه مغتوانند حالت پالس را تغممر دهند.
اجررازه دهمررد بررا روش دوم MyClass.classmethod ،آشررنا شررویم .ایررن متررد را بررا
دپوریتور @classmethodبرای نشان دادن متد پالس عالمتگذاری پردیم.
به جای آرگومان ورودی ،selfمتدهای پالس آرگومان clsرا دریافت مغپنند په
وقتغ این متد فراخوانغ مغشود به پالس اشاره مغپند و به یک نمونه شغء اشاره نمرغ
پند.
چون متدهای پالس فقط بره ایرن آرگومران clsدسترسرغ دارنرد ،پرس نمرغتواننرد
وضعمت نمونه شغء را تغممر دهند .این پار مستلزم دسترسغ به selfاست .برا ایرن حرال،
متدهای پالس مغتوانند وضعمت پالس را په به تمام نمونههای پالس اعمال مغشود،
اصالح پنند.
1 state
فصل چهارم :کالسها و برنامهنویسی شیء گرا 159
آن دسترسغ داشته باشند ،هستند .آنهرا عمردتا روشرغ بررای فضرای نرام متردهای شرما
مغباشند.
این پد نشان مغدهد په متد نمونه از طریق آرگومان selfبه نمونه شغء (چاپ شده
بصورت > )<MyClass instanceدسترسغ دارد.
هنگام فراخوانغ متد ،پایتون آرگومان selfرا با شغء نمونه obj ،جرایگزین مغپنرد.
مغتوانمم فرمت تغممر یافتره برا سرمنتاس فراخروانغ نقطرهای )( 2obj.methodرا نادیرده
بگمریم و آن را به صورت دستغ فراخوانغ پنمم تا نتمزه مشابه را بدست آوریم.
)>>> 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(['mozzarella', 'tomatoes
)]'Pizza(['mozzarella', 'tomatoes', 'ham', 'mushrooms
)Pizza(['mozzarella'] * 4
ایتالمایغها قرن ها پمی پمتزا را پشف پردند ،پس انروا خوشرمزه پمترزا برا نرامهرای
آنها است .در صورت بهرهمندی از نام آنها ،عملارد بهتری خرواهمم داشرت و خا
به پاربران پالس پمتزا خود ،رابط بهتر برای ایزاد اشماء پمتزایغ په طلب مغپننرد ارائره
مغدهمم.
روشغ خوب و تممز برای انزام این پار با استفاده از متدهای پالس به صورت توابع
پارخانه 1برای انوا مختلف پمتزایغ په مغتوانمم ایزاد پنمم است.
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در هر پالس استفاده مغپند .استفاده از متردهای
پالس ،افزودن سازندههای جایگزین بمشتر را در صورت لزوم اماانپذیر مغپنرد .ایرن
پار مغتواند رابطغ برای خوداستنادی پالسهای شما (تا انردازه معمنرغ) و سرادهسرازی
پاربرد آنها ایزاد پند.
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تبدیل پردم.
اجازه دهمد این روش را بررسغ پنمم.
قطعا ،این هنوز یک مثال ساده است ،اما بره توضرمح برخرغ از مزایرایغ پره متردهای
استاتمک ارائه مغدهند پمک مغپند.
طبق آموختههای ما ،متدهای استاتمک نمغتوانند به حالرت پرالس یرا حالرت نمونره
دسترسغ پمدا پنند ،چون آرگومان clsیرا selfرا دریافرت نمرغپننرد .ایرن محردودیت
از بزرگغ است .همچنمن سمگنالغ عالغ برای نشان دادن مستقل بودن این متدهای خا
هر چمز پمرامون آن است.
در مثال فوق ،مشخص است په متد )( circle_areaبه همک وجه نمغتواند پالس یرا
نمونه پالس را اصالح پند( .قطعا ،همواره مغتوانمد پمرامون آن با متغمر پلغ پار پنمد،
اما در اینزا موضو مهمغ نمست).
حاال ،چرا این پار حائز اهممت است؟
166ترفندهای پایتون
نام گذاری یک مترد بره عنروان مترد اسرتاتمک ،فقرط نشرانه ایرن نمسرت پره آن مترد
نمغتواند حالت پالس یا حالت نمونه را اصالح پند .همانطور په مشاهده پردید ،ایرن
محدودیت در زمان اجرای پایتون نمز اعمال مغشود.
تانمک هایغ مانند این ،اماان برقراری ارتبراط مسرتقمم برا بخری هرایغ از معمراری
پالس را فرراهم مرغپننرد ترا پرار توسرعه جدیرد بره صرورت طبمعرغ و برا وجرود ایرن
محدودیتها انزام شود .البته ،حذف این محدودیت ها پار بسمار آسانغ خواهد بود .اما
در عمل ،آنها به جلوگمری از اصالحات تصرادفغ پره بررخالف طررح اصرلغ هسرتند،
پمک مغپنند.
به بمانغ متفاوت ،استفاده از متدهای استاتمک و متردهای پرالس ،روش هرایغ بررای
برقراری ارتباط با هدف برنامهنویس هستند و هدف را به اندازه مناسب مشخص مغپنند
تا از اشتباهات و اشااالت "فراموشغ" په طرح را خراب مغپند ،اجتناب شود.
با پاربرد به ممزان پم و منطقغ ،نوشتن برخغ متدها به این صورت مغتوانند مزایرای
نگهداری را فراهم پنند و احتمال استفاده ناصحمح سایر برنامرهنرویسهرا از پالسهرای
شما را پاهی دهند.
همچنمن متدهای استاتمک هنگام نوشتن یونمرت تسرتها مزایرایغ دارنرد .چرون مترد
)( circle_areaپامالد مستقل از بقمه پالس است ،پس آزمایی آن بسمار سادهتر خواهد
بود.
از طرف دیگر ،این پار فرآیندهای آتغ نگهداری را آسانتر مغپند و پمونردی برمن
سبک برنامهنویسغ شغگرا و رویهای 1ایزاد مغپند.
• متدهای پالس به نمونه پالس نمرازی ندارنرد .آنهرا نمرغتواننرد بره نمونره ()self
دسترسغ پمدا پنند اما از طریق clsبه خود پالس دسترسغ دارند.
• متدهای استاتمک به clsیا selfدسترسغ ندارند .آنها مانند توابع معمولغ پار مرغ
پنند ،اما متعلق به فضای نام پالس هستند.
• متدهای استاتمک و پالس با یادیگر ارتباط برقرار مغپننرد و (ترا انردازه معمنرغ)
هدف برنامهنویس را درباره طراحغ پالس اجراء مغپننرد .ایرن امرر مرغتوانرد مزایرای
قطعغ در زممنه نگهداری از سمستم را ایزاد پند.
فصل پنزم :ساختارهای داده در پایتون
فصل پنجم
1 object
2 key
فصل پنجم :ساختارهای داده در پایتون 173
{ = phonebook
'bob': 7387,
'alice': 3719,
'jack': 7052,
}
]'>>> 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در پایتون
هستند په در پنار تمام ویژگغهای این پالس ،یاسری ویژگغهای اضافه تر را نمرز بره
همراه خود دارند .در ادامه به برخغ از آنها نگاهغ مغاندازیم.
>>> 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'])
>>> dd['dogs']
['Rufus', 'Kathrin', 'Mr Sniffles']
176ترفندهای پایتون
>>> chain
ChainMap({'one': 1, 'two': 2}, {'three': 3, 'four':
)}4
1 Array
178ترفندهای پایتون
در این بخی نگاهغ به پماده سازی آرایهها در پایتون مغانردازیم و پتابخانرههایغ را
در زبان پایتون بررسغ مغپنمم په آرایهها را به نحوی دیگر در ایرن زبران پمراده سرازی
نمودهاند .بنابراین شما با نقاط قوت و ضرعف هرپردام از آنهرا آشرنا شرده و مغتوانمرد
تصممم بگمرید په از پدام یک در برنامههای خود استفاده پنمد .اما بهتر اسرت ابتردا بره
توضمحغ دربارهی ماهمت آرایهها بپردازیم.
آرایهها از تعداد ابتغ از مقادیر تشامل شدهاند په به هر پدام از این مقادیر مغتوان
با استفاده از اندیس آن دسترسغ داشت .از آنزا په آرایهها اطالعات را در بلوکهرایغ
همزرروار در حافظرره ( )memoryذخمررره مغپننررد ،آنهررا را برره عنرروان سرراختار دادهی
همزوار در نظر مغگمرند.
یک مثال واقعغ برای آرایهها یک پارپمنگ است :مغتوان بره پارپمنرگ بره عنروان
یک آبزات نگاه پرد .در پارپمنگ نقاطغ وجود دارد په با شمارهای منحصر بره فررد
وسایل نقلمه بوده په ممان است خرالغ یرا دارای مشخص شدهاند .این نقاط ،مخصو
یک ماشمن یا موترور باشرند .امرا تمرام پارپمنگهرا بره یرک صرورت نمسرتند .برخرغ از
نو خاصغ از وسایل نقلمه هستند .این نو از پارپمنگهرا را پارپمنگها تنها مخصو
مغتوان با آرایههایغ مقایسه پرد په تنها یک نو داده 1را در خود جای دادهاند.
از لحاظ عملارد ،دسترسغ به عناصر یک آرایه با استفاده از اندیس آن بسمار سرریع
بوده و هزینهای برابر با ) O(1دارد.
آرایهها در پایتون و پتابخانههای اسرتاندارد آن بره گونرههای مختلفرغ پمراده سرازی
شدهاند په در ادامه نگاهغ به انوا آنها خواهمم انداخت.
2
- list .1-2-5آرایههای تغییرپذیر
لمست در پایتون جزئغ از هستهی اصلغ این زبان است .علغررم نام آن ،از لمست در
پایتون به عنوان آرایههایغ تغممر پذیر استفاده مغشود.
این بدان معناست په مغتوان به یک لمست در پرایتون ،عناصرری را اضرافه یرا از آن
حذف نمود و این لمست در پشت صحنه به طور خودپار فضای حافظرهای را بررای ایرن
مغدهد یا فضای حافظهای را در هنگام حذف یک عنصرر آزاد عناصر جدید اختصا
مغپند.
یک لمست در پایتون مغتواند مقردار دلخرواهغ از عناصرر را در خرود نگره دارد .در
پایتون همه چمز به عنوان آبزات در نظر گرفته مغشرود .بنرابراین شرما مغتوانمرد یرک
لمست بسازید په شامل انوا مختلفغ از دادهها است .شاید بتوان به این قابلمت به عنروان
ناتهی قوت لمستها نگاه پرد؛ اما در واقع با این پار ،یک لمست مزبرور اسرت فضرای
بمشتری از حافظه را اشغال پند.
1
- tuple .2-2-5آرایههای تغییر ناپذیر
همانند لمستها ،تاپلها نمز جزء هستهی اصلغ زبان پایتون هستند .اما برخالف لمست،
تاپلها تغممر ناپذیرند .بدین معنغ په نمغتوان عنصری را از یک تاپرل حرذف یرا بره آن
اضافه نمود .تمام عناصر موجود در تاپل باید در هنگام ساخت آن تعریف شوند.
تاپلها نمز مغتوانند انوا مختلفرغ از دادههرا را درون خرود نگره دارنرد .امرا هماننرد
لمستها ،این عمل باعث مغشود تا یک تاپل فضای حافظهی بمشتری اشغال پند.
1 immutable
فصل پنجم :ساختارهای داده در پایتون 181
پایتون در ذخمره سازی نو دادههرا اسرت .بردین معنرا پره در ایرن نرو از آرایرهها تنهرا
مغتوان عناصری از یک نو داده ذخمره نمود.
به دلمل این محدودیت موجود در ،array.arrayاین نو از آرایهها فضرای پمترری
را در مقایسه با لمست یا تاپل در حافظه اشغال مغپنند.
همچنمن آرایهها ،از متردهایغ مشرابه برا لمسرت نمرز پشرتمبانغ مغپننرد .بنرابراین شرما
مغتوانمد از این آرایهها به عنوان جایگزینغ برای لمسرتها در برنامرههای خرود اسرتفاده
پنمد؛ بدون ایناه نمازی به تغممر اساسغ در سایر قسمتهای برنامهی خود داشته باشمد.
)>>> arr.append(42.0
>>> arr
)]array('f', [1.0, 2.0, 2.5, 42.0
>>> arr
''abcd
>>> arr[1]
23
>>> arr.append(42)
>>> arr
bytearray(b'\x00\x02\x03*')
1
struct .3-5ها و اشیاء انتقال داده
در مقایسه با آرایهها ،ساختارهای دادهی رپورد دارای مقداری فملد ابت هستند پره
هر فملد مغتواند یک نام داشته و نو دادههای آن ها با یادیگر تفاوت داشته باشد.
در این بخی ،با نحوهی پماده سازی رپوردها و structها در پایتون آشرنا مغشرویم.
برای این پار تنها از پتابخانههای استاندارد پایتون استفاده خواهمم نمود.
پایتون انوا دادههای مختلفغ را برای ما فراهم آورده است په به مرا ایرن قابلمرت را
مغدهد تا بتوانمم رپوردهاstruct ،ها و آبزاتهای انتقال داده را پمراده سرازی نمرایمم.
در این بخی یک نگاه پلغ به تمام آنها و ویژگغهای هریک خواهمم انداخت.
car1 = {
'color': 'red',
'mileage': 3812.4,
'automatic': True,
}
car2 = {
'color': 'blue',
'mileage': 40231,
'automatic': False,
}
# Get mileage:
>>> car2['mileage']
40231
از لحاظ عملارد ،تاپلها از فضای حافظهی پمتری نسبت به listها در پایتون استفاده
پرده و همچنمن سریع تر نمز هستند.
1
همانطور په در پد زیر مشاهده مغپنمد ،برای ساخت یک تاپل از تنها یک آپارد
LOAD_CONSTاستفاده شده است .در صورتغ په برای سراخت یرک لمسرت برا همران
محتویات به عملمات بمشتری نماز است.
البته شما نبایرد بررای تفراوت برمن ایرن دو اهممرت زیرادی قائرل شروید .در برخرغ از
پروژهها تفاوت عملارد بمن این دو بسمار ناچمز است.
شاید بتوان گفت یاغ از نقاط ضعف در تاپلها ،توانرایغ دسترسرغ بره فملردهای آن
تنها با استفاده از شمارهی اندیس آن فملد اسرت .شرما نمغتوانمرد هماننرد دیاشرنری بره
دهمد .شاید بتوان گفت این عمل خوانایغ پد شما فملدهایتان در تاپل یک نام اختصا
را پمغ تحت تا مر قرار خواهد داد.
همچنمن گاهغ اوقات پمغ سخت است په بتوان اطممنان حاصل نمود پره دو تاپرل
دارای تعداد فملد برابر بوده و ویژگغهای ذخمره شده در آن دو به یرک صرورت اسرت.
1 opcode
190ترفندهای پایتون
بنابراین پمشنهاد مغشود ترا در صرورت اماران فملردهای پمترری در یرک تاپرل ذخمرره
نمایمد.
فملدهای ذخمره شده در یک پالس قابل تغممر هستند و همچنمن بره راحترغ مغتروان
دپوریترور1 فملدهای جدیدی به یک پالس اضافه نمود .البته شما مغتوانمد با استفاده از
propertyفملدهایغ فقط خواندنغ نمز ایزاد پنمد.
نوشتن یک پالس این قابلمرت را بره مرا مغدهرد ترا بتروانمم متردهای مختلفرغ بررای
آبزاتهای یک پالس تعریف پرده و رفتار دادههای خود را شخصغ سازی پنمم.
class Car:
def __init__(self, color, mileage, automatic):
self.color = color
self.mileage = mileage
self.automatic = automatic
1 Decorator
192ترفندهای پایتون
namedtupleها همانند tupleها تغممر ناپذیر هستند .این بدان معناسرت پره پرس از
ساخت یک نمونه از ،namedtupleشما نمغتوانمرد فملردهایغ را بره آن اضرافه پررده،
تغممر دهمد و یا از آن حذف نمایمد.
همررانطور پرره از اسررم namedtupleمشررخص اسررت ،برره هررر عنصررر از یررک
namedtupleمغتوان به وسملهی نام آن دسترسغ پمدا پررد .طبمعترا دسترسرغ بره یرک
عنصر با استفاده از نام آن ،ساده تر از دسترسغ به آن با پمک عدد اندیس مربوط به آن
عنصر است.
namedtupleبه همان اندازهی tupleها حافظه اشغال مغپند پره مقردار آن بسرمار
پمتر از مقدار حافظهی اشغال شده توسط یک پالس در پایتون است.
)>>> getsizeof(p1
72
)>>> getsizeof(p2
72
# Accessing fields:
>>> car1.mileage
3812.4
1 mypy-lang.org
ترفندهای پایتون 194
class Car(NamedTuple):
color: str
mileage: float
automatic: bool
# Accessing fields:
>>> car1.mileage
3812.4
از Structها در پایتون به ندرت به منظور نمایی اشما داده استفاده مغشود .ایرن نرو
دادهها بمشتر به منظور روشغ برای تبادل فرمت دادهها بمن زبان پایتون و Cدر نظر گرفته
مغشوند.
در صورتغ په قصد دارید تا همه چمز را بره صرورت سراده و خوانرا بررای تمرام
برنامه نویسان دیگر پماده سازی نمایمد ،بهتر اسرت ترا از دیاشرنریها در پرایتون
استفاده پنمد.
در صورتغ په قصد دارید متدهایغ را برای اشماء دادهی خرود در نظرر بگمریرد،
بهترررر اسرررت یرررک پرررالس بسرررازید یرررا از collections.namedtupleیرررا
typing.NamedTupleبرای ساخت پالس خود ارث بری پنمد.
در صورتغ په قصد دارید اشماء دادهی خود را در سطح یک شباه انتقال دهمرد
یا آنها را در دیسک ذخمره نمایمد ،پمشنهاد مغشود از struct.Structبرای این
پار استفاده نمایمد.
به طور پلغ در صورتغ په به دنبال یک راه هممشرگغ و سراده بررای ذخمرره سرازی
اشرررماء داده هسرررتمد ،بهترررر اسرررت از collections.namedtupleدر پرررایتون 2و از
typing.NamedTupleدر پایتون 3استفاده پنمد.
.4-5مجموعهها در پایتون
در این بخی در مورد نحوهی پماده سازی مزموعههای تغممر پذیر و تغممر ناپرذیر در
پایتون صحبت خواهمم نمود .بهتر است ابتدا در مورد مزموعهها در پایتون بمشتر بدانمم:
یک مزموعه ( ،)setاجتماعغ از اشماء بدون ترتمب در یک سراختار داده اسرت پره
نمغتوان عناصری تاراری درون آن قرار داد .معموال از مزموعهها در پایتون به منظرور
تست وجود یا عدم وجود یک عنصر در یک ساختار داده استفاده مغشود .البته الزم بره
ذپر است په مغتوان عناصری نمز به یک مزموعه اضافه یا از آن حذف نمود .همچنمن
مغتوان اجتما یا اشتراک دو مزموعه را با استفاده از این ساختار داده محاسبه نمود.
هزینهی چک پردن عضویت یرک عنصرر در مزموعرهای در پرایتون برابرر برا )O(1
است .عملمات اتحاد یا اجتمرا دو مزموعره و همچنرمن تفراوت دو مزموعره و بدسرت
در پایتون به طور ممانگمن هزینهای برابر با ) O(nدارد. آوردن زیر مزموعهای خا
198ترفندهای پایتون
توجه داشته باشمد په به منظور ساخت یک مزموعهی خالغ در پایتون باید از دستور
)( setاستفاده پنمد .از {} برای ایزاد یک دیاشنری خالغ در پایتون استفاده مغشود.
پتابخانههای استاندارد موجود در پایتون انوا مختلفغ از این نو داده را پماده سازی
پردهاند په در ادامه نگاهغ به آنها مغاندازیم.
)'>>> vowels.add('x
>>> vowels
}'{'i', 'a', 'u', 'o', 'x', 'e
)>>> len(vowels
6
فصل پنجم :ساختارهای داده در پایتون 199
1
- collections.Counter .3-4-5مجموعههای چندتایی
این پالس در پایتون یک مزموعهی چنردتایغ را پمراده سرازی مغپنرد پره عناصرر
موجود در یک مزموعه مغتوانند بمی از یک عنصر داشته باشند.
با استفاده از این نو مزموعهها مغتوان وجود یا عدم وجود یک عنصر در مزموعه
را بررسغ پرده و همچنمن مغتوان تعداد دفعرات رخرداد آن عنصرر را در مزموعره نمرز
بدست آورد.
1 multisets
200ترفندهای پایتون
توجه داشته باشمد په تابع lenروی این پالس ،تعداد عناصر منحصرر بره فررد را بره
شما نمایی مغدهد .برای بدست آوردن مزمو تمام عناصر موجرود در ایرن multiset
مغتوانمد از تابع sumاستفاده نمایمد.
)>>> len(inventory
3 # Unique elements
))(>>> sum(inventory.values
6 # Total no. of elements
1 stack
2 queue
3 depth-first search
202ترفندهای پایتون
>>> s
]'['eat', 'sleep', 'code
)(>>> s.pop
''code
)(>>> s.pop
''sleep
)(>>> s.pop
''eat
)(>>> s.pop
"IndexError: "pop from empty list
انتهای آن با هزینهی ) O(1انزام داد .این قابلمت باعث مغشود په بتروان از ایرن پرالس
هم به عنوان پشته و هم به عنوان صف در پایتون استفاده پرد.
نقطهی ضعف این پالس در دسترسغ بره عنصرری در وسرط آن اسرت پره هزینرهی
برابر با ) O(nبرای ما دارد.
به طور پلغ dequeمغتواند بهترین انتخاب شما برای پار با پشتهها در برنامرههایتان
باشد.
>>> s
)]'deque(['eat', 'sleep', 'code
)(>>> s.pop
''code
)(>>> s.pop
''sleep
)(>>> s.pop
''eat
)(>>> s.pop
"IndexError: "pop from an empty deque
>>> 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...
برای ایناار باید از متدهای appendو popاستفاده پنمد ترا هزینرهای مناسرب برابرر برا
) O(1برای عملمات خود داشته باشمد.
پالس collections.dequeدر پایتون به صورت یک پشته با دو جهت پماده سرازی
شده است په عملمات حذف یا افزودن عنصری به آن را مغتوان از هر سمت انزام داده
و هزینهای برابر با ) O(1خواهد داشت.
به طور پلغ بهتر است برای پماده سرازی پشرتهها در پرایتون از collections.deque
استفاده پنمم.
1 Queue
206ترفندهای پایتون
از لحاظ عملارد ،عملمات حذف و افزودن عنصری به یک صرف هزینرهای برابرر برا
) O(1دارد و بنابراین این عملمات سریع انزام مغشود.
از صفها در بسماری از برنامهها و الگوریتمها در علروم پرامپموتر اسرتفاده مغشرود.
یاغ از الگوریتمهای رایزغ په از ایرن سراختار داده اسرتفاده مغپنرد ،الگروریتم BFS1
است.
الگوریتمهررای زمرران بنرردی در پررامپموتر معمرروال از صررفهای بررا اولویررت اسررتفاده
مغپنند .برخالف صفهای عادی په عناصر را بر اساس ترتمرب ورود از صرف خرارج
مغپند ،صفهای با اولویت در هنگام خارج پرردن یرک عنصرر از صرف بره اولویرت
بره هرر عناصر توجه مغپند .اولویت عناصر در یک صف بر اساس پلمردی مخصرو
عنصر مشرخص مغشرود .در بخری آینرده بره توضرمحات بمشرتری در مرورد صرفهای
اولویت دار مغپردازیم.
صفهای عادی در پایتون به صورتهای مختلفغ پماده سرازی شردهاند .در ادامره بره
بررسغ هرپدام خواهمم پرداخت.
>>> q
]'['eat', 'sleep', 'code
>>> q
)]'deque(['eat', 'sleep', 'code
)(>>> q.popleft
''eat
)(>>> q.popleft
''sleep
)(>>> q.popleft
''code
)(>>> q.popleft
"IndexError: "pop from an empty deque
>>> 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...
>>> q
><multiprocessing.queues.Queue object at 0x1081c12b0
)(>>> q.get
''eat
)(>>> q.get
''sleep
)(>>> q.get
''code
)(>>> q.get
# Blocks / waits forever...
.7-5صفهای با اولویت
صفهای دارای اولویت ،نوعغ از صفها هستند په در آنها هر عنصر دارای یرک
پلمد بوده په این پلمد مشخص پنندهی اولویت عنصر بروده و معمروال بره عنروان یرک
مغیابد. ارزش وزنغ به عناصر اختصا
فصل پنجم :ساختارهای داده در پایتون 211
در واقررع در ایررن نررو از صررفها بزررای آنپرره اولررمن عنصررر وارد شررده در هنگررام
فراخوانغ از صف خارج گردد ،عنصری با بمشترین اولویت در هنگام فراخوانغ از صف
خارج مغگردد.
از این صفها برای مسائل مختلف زمانبندی استفاده مغشرود پره مشرخص مغپنرد
پدام برنامه اولویت بمشتری داشته و باید زودتر مورد پردازش قرار گمرد .به طور معمول
برنامههایغ با اولویت باال روی سمستم ،باید نسبت به برنامههایغ با اولویت پمترر سرریعتر
پردازش شوند .با ساماندهغ این برنامهها در صفهای با اولویت ،سمستم عامرل مغتوانرد
برنامههایغ با اولویرت براال را انتخراب پررده و آنهرا را زودترر از سرایر برنامرهها مرورد
پردازش قرار دهد.
در این بخی با انوا مختلفغ از صفهای با اولویت در پایتون و چگرونگغ نحروهی
استفاده از آنها در برنامههای خود آشنا مغشویم.
][ = q
))'q.append((2, 'code
))'q.append((1, 'eat
))'q.append((3, 'sleep
while q:
)(next_item = q.pop
)print(next_item
# Result:
# )'(1, 'eat
# )'(2, 'code
# )'(3, 'sleep
import heapq
][ = q
while q:
)next_item = heapq.heappop(q
)print(next_item
# Result:
# )'(1, 'eat
# )'(2, 'code
# )'(3, 'sleep
)(q = PriorityQueue
))'q.put((2, 'code
))'q.put((1, 'eat
))'q.put((3, 'sleep
# Result:
# )'(1, 'eat
# )'(2, 'code
# )'(3, 'sleep
فصل ششم
حلقهها در پایتون
.1-6نوشتن حلقههای پایتونی
دانستن چگونگغ نوشتن حلقهها یاغ از اساسغ ترین موارد برای برنامه نویسغ در هر
زبانغ است .اما نحوهی نوشتن حلقهها در پایتون پمغ برا سرایر زبانهرا ماننرد Cیرا Java
متفاوت است.
برای مثال در قطعه پد زیر ،برنامه نویس سعغ پرده است یک حلقه را هماننرد زبران
Cیا Javaبنویسد.
i = 0
حال سؤال پمی مغآید په چرا پد باال به صورت پایتونغ نوشته نشده است؟
ناتهی اول ایناه برنامه نویس اندیس iرا به صورت دستغ ایزراد پررده و هربرار در
انتهای حلقه آن را افزایی مغدهد؛ و ناته دوم :برنامه نویس برای بدسرت آوردن طرول
لمست از تابع lenاستفاده پرده است.
ما در پایتون مغتوانمم حلقهها را به صورتغ بسمار ساده تر پمراده سرازی پنرمم پره دو
عمل باال را به صورت خودپار برای ما انزام مغدهد .بدین ترتمرب الزم نمسرت انردیس
یک لمست را به صورت دسرتغ ایزراد پررده و آن را مردیریت پنمرد .بنرابراین حلقرهی
پایتونغ ما بسمار ساده تر و خواناتر مغشود.
حال حلقهی باال را بهبود مغبخشمم و آن را به صورت پایتونغ پماده سرازی خرواهمم
نمود .برای این پار از تابع درونغ rangeاستفاده مغپنمم په بصورت خودپار انردیس
را برای ما مدیریت مغپند.
218ترفندهای پایتون
))>>> range(len(my_items
)range(0, 3
نتمزهی حاصل شده از rangeیک نو دادهی تغممر ناپذیر اسرت .مزیرت اسرتفاده از
rangeبزای listدر اشغال فضای حافظهی پمتر است range .مقادیر یک لمست را بره
صورت جداگانه ذخمره نمغپند و تنها به صورت یک شغء تارار شونده پار مغپند.
for i in range(len(my_items)):
)]print(my_items[i
حتغ پد باال را باز هم مغتوان بهبرود بخشرمد .چررا پره اسرتفاده از rangeو lenبره
منظور ایزاد یک حلقه در پایتون مناسب نمست .بنابراین بمایمرد برازهم پرد براال را تغممرر
دهمم.
همانطور په قبال هم به آن اشاره پردیم ،هرر حلقره در پرایتون بره صرورت خودپرار
اندیس یک لمست را مدیریت مغپند و به صورت یک حلقهی for-eachعمل مغپند.
شاید بهتر باشد پد باال را به صورت زیر بنویسمم.
قطعه پد باال ،مناسب ترین روش برای ایزاد یک حلقه در پایتون است .همانطور په
مشاهده مغپنمد در این حلقه بدون توجره بره انردیس یرک لمسرت ،مغتروان روی تمرام
مقادیر موجود در لمست پممایی انزام داد .همچنمن توجه داشته باشمد په مقادیر موجود
در لمست به همان ترتمبغ په قرار دارند در حلقه مورد پممایی قرار مغگمرند.
شاید در یک برنامه ما نماز به اندیس هر عنصر موجود در لمست نمرز داشرته باشرمم .برا
قطعه پد باال نمغتوانمم به اندیس موجود دسترسغ داشته باشمم.
فصل ششم :حلقهها در پایتون 219
در پایتون بدون استفاده از rangeو lenهم مغتوان حلقههایغ ایزاد پرد په اندیس
عناصر را نمز مدیریت مغپند .برا اسرتفاده از ترابع درونرغ enumerateمغتروانمم روی
مقادیر و اندیس آنها در یک لمست پممایی انزام دهمم.
0: a
1: b
2: c
همانطور په مشاهده مغپنمد ،در قطعه پد باال مغتوانمم در یک حلقره بره عناصرر و
اندیس آنها دسترسغ داشته باشمم .همچنمن در پایتون مغتروانمم روی پلمردها و مقرادیر
موجود در دیاشنری نمز پممایی انزام داده و برای آن یک حلقه ایزاد نمایمم.
{ = >>> emails
'Bob': '[email protected]',
'Alice': '[email protected]',
}
در انتهای این بخی الزم است تا در مورد یک موضرو مهرم دیگرر در حلقرهها نمرز
صحبت پنمم .شاید شما قصد داشته باشمد تا یک حلقه ایزاد پرده و اندازهی گام حلقه
( )step sizeرا نمز بخواهمد در آن مدیریت پنمد .برای نمونه حلقهی جاوایغ زیرر را در
نظر بگمرید.
قطعه پد باال به چه صورت باید در پایتون نوشته شود؟ در این مواقرع بایرد دوبراره از
تابع rangeاستفاده پنمم .این تابع مغتوانرد مقرادیری بره عنروان نقطرهی شررو ()start
برای حلقه ،نقطهی پایان ( )stopو اندازهی گام ( )stepرا از برنامه نویس دریافت پنرد.
بنابراین قطعه پد جاوایغ باال به صورت زیر به یک قطعه پد پایتون تبدیل مغشود.
>>> squares
][0, 1, 4, 9, 16, 25, 36, 49, 64, 81
فصل ششم :حلقهها در پایتون 221
در صورتغ په بخواهمد این لمست را برا اسرتفاده از حلقرهی forدر پرایتون بنویسرمد،
قطعه پد آن به صورت زیر خواهد بود.
این قطعه پد نمز بسمار ساده است .با یک بررسغ سراده و مقایسرهی ایرن دو روش برا
یادیگر مغتوانمم به ساختار ایزاد لمست در یک خرط پرغ ببرریم .بره صرورت پلرغ بره
منظور ایزاد یک لمست طبق یک قاعدهی مشخص ،باید به صورت زیر عمل پنمم.
خط باال ،مغتواند به صورت یک حلقهی forهمانند قطعه پد زیر نمز نوشته شود.
][ = values
for item in collection:
)values.append(expression
در قطعه پد باال ،ابتدا یک لمست خالغ ایزاد نمرودیم ترا در ادامره مقرادیر را بره آن
اضافه نمایمم .سپس روی تمام اعضای مزموعه ( )collectionsپممایی پررده و حاصرل
را به لمست اضافه مغپنمم .این یک قاعدهی پلغ به منظور ایزاد یک لمست طبرق یرک
الگوی مشخص است په مغتوان از آن در نوشتن برنامههای خود استفاده پنمم.
حال بهتر است یک قدم جلوتر رفته و شرط ها را نمز به این حلقه اضافه پنمم.
ما مغتوانمم در هنگام ایزاد لمست خود در یک خط دستوری پایتون ،به آن شرط نمز
اضافه نمایمم تا تصممم گمری شود پدام مقادیر به لمست افزوده شوند.
در لمست باال ،تنها مربع اعدادی ( 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
بر خالف لمستها در پایتون په ترتمب افزوده شدن عناصرر را نمرز نگره مرغدارد ،در
مزموعه ،عناصر به صورت نامرتب وجود دارند.
همچنمن برای ایزاد یک دیاشنری نمز به صورت زیر عمل مغپنمم.
بهتر است در هنگام پد نویسغ به این صورت ،به یک ناته توجه داشته باشمد .هرچه
بمشتر در این روش مهارت داشته باشمد و بتوانمد لمستها یا دیاشنریهای پمچمده ایزراد
پنمد ،ممان است خواندن پد شما برای دیگران پمرغ سرخت باشرد .پرس بهترر اسرت
هممشه و همه جا از این روش استفاده نانمد.
به طور پلغ پمشنهاد مغشرود بمشرتر از همران روش قردیمغ (نوشرتن حلقرههای )for
استفاده شود .چرا په نگهداری برنامه و خوانایغ آن بمشتر است.
1
.3-6ترفندهایی برای برش لیستها و استفاده از عملگر سوشی
در پایتون مغتوان یک لمست را تاه تاه نمود .به این عمل در پرایتون slicingگفتره
مغشود .با استفاده از این روش مغتوان تنها به بخشغ از یک لمست په مورد نمراز اسرت
1 Sushi
224ترفندهای پایتون
دسترسغ پمدا نمود .برای مثال مغتوان یک لمست بزرگ را با این روش به چندین لمست
پوچاتر تقسمم پرد.
برای ایناار از عالمت براپت [] استفاده مغشود پره قاعردهی پلرغ آن بره صرورت
[ ]start:stop:stepاست.
]# 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.clearنمز ایرن پرار را انزرام دهمرد .البتره
توجه داشته باشمد په این متد تنها در پایتون 3قابل استفاده است.
عالوه بر این ،مغتوانمم تمام عناصر یک لمست را برا ایرن روش ،یرا مقرادیری جدیرد
جایگزین پنمم .به مثال صفحه بعد توجه پنمد.
226ترفندهای پایتون
در قطعه پد باال ،مقادیری جدید در lstبا مقادیر قبلغ جرایگزین شرد؛ امرا متغمرر lst
حذف نشد .به هممن دلمل متغمرر original_lstنمرز پرس از آن دسرتخوش تغممرر شرده و
همان مقادیر lstرا در خود نگه داشته است.
یاغ دیگر از پاربردهای عملگر سوشغ این است په با استفاده از آن مغتروان یرک
پپغ از تمام محتویات (نه متغمر) یک لمست ایزاد نمود .به مثال زیر توجه پنمد.
در قطعه پد باال ،تنها محتویات موجود در لمست را در متغمری جدید پپرغ نمرودیم.
به هممن دلمل است په ماهمت دو متغمر lstو copied_lstبا یادیگر برابر نمستند.
اما این حلقه در پشت صحنه چگونه پار مغپند؟ چگونه پایتون تشخمص مغدهد په
یک شغء قابل پممایی با حلقه است یا خمر؟
در واقع اشمایغ پره دو مترد __ __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
Hello
Hello
Hello
Hello
Hello
...
در ادامه به بررسغ دقمق تر این مثال خواهمم پرداخت تا بهتر برا نحروهی پرارپرد دو
متد __ __iterو __ __nextآشنا شویم.
)'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در پایتون استفاده پردیم.
بدین صورت در هر بار استفاده از تابع nextهمران پلمرهی Helloبررای مرا نمرایی
داده مغشود .همانطور په مشاهده مغپنمد ،در این قطعه پد بزرای اسرتفاده از متردهای
__ __iterو __ __nextدر پرررالس ،Repeaterاز توابرررع nextو iterدر پرررایتون
استفاده شده است .در واقع این توابع همان پرار را بررای مرا انزرام مغدهنرد و از لحراظ
عملارد تفاوتغ با حالت قبل ندارند؛ فقط در این حالرت ،خوانرایغ پرد مرا بهبرود یافتره
است.
این قابلمت برای برخغ دیگر از توابع داخلغ در پایتون نمرز وجرود دارد .بررای مثرال،
استفاده از ) len(xدقمقا برابر با فراخوانغ مترد __ x.__lenاسرت .اصروال اسرتفاده از ایرن
توابع در پایتون (بزای فراخوانغ داندر متدها به صورت مستقمم) در هنگرام پرد نویسرغ
بمشتر پاربرد داشته و به خوانایغ پد شما پمک مغپند.
پار مغپنند .اپثر اوقات مغتوان پارایغ این دو پالس را در یک پالس ادررام پررد.
به این صورت ،پماده سازی پالس ما ساده تر شده و نماز به پد نویسغ پمتری دارد.
پالس 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
در واقع در بخیهای ابتدایغ این فصل ما قصد داشتمم این عمل را با پماده سازی دو
پالس انزام دهمم تا بهتر و دقمق تر مفاهمم را درک پنرمم .امرا در ادامره از ایرن پرالس
استفاده خواهمم نمود.
این پد اعداد 2 ،1و 3موجود در لمست را چراپ پررده و سرپس متوقرف مغشرود.
یعنغ دیگر نمازی به فشردن پلمد ترپمبغ Ctrl+Cبرای متوقف نمودن برنامه و قطع حلقه
نمست.
در این بخی قصد داریم تا یک پالس iteratorپماده سازی نمرایمم ترا بره صرورت
خودپار متوقف شود و به صورت بغ نهایت یک مقردار را در خروجرغ نمرایی ندهرد.
بدین منظور پالسغ با نام BoundedRepeaterپماده سازی مغپنمم .این پرالس بسرمار
شبمه به همان پالس Repeaterدر بخی قبل است.
اما ایناار باید به چه صورت انزام شرود؟ تشرخمص انتهرای لمسرتغ از مقرادیر توسرط
پایتون به چه صورت انزام مغشود؟
بماید به همان قطعه پد قبل نگاهغ دوباره بمندازیم .از تابع iterدر پایتون برای ایناار
پمک مغگمریم تا ببمنمم پایتون iterateروی یک لمسرت را بره چره صرورت مردیریت
مغپند.
234ترفندهای پایتون
در قطعه پد باال سه مقدار حاصل در لمست را بدست آوردیرم .حرال اگرر دوبراره از
تابع 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 در پالس براال دفعرات تاررار برا پرارامتر
.استفاده از این پالس به صورت زیر مغتوانمم عمل پنمم
Hello
Hello
Hello
repeater = BoundedRepeater('Hello', 3)
iterator = iter(repeater)
while True:
try:
item = next(iterator)
except StopIteration:
break
print(item)
236ترفندهای پایتون
در قطعه پد باال ،در هر بار خطای StopIterationبه منظور تشرخمص انتهرای حلقره
بررسغ مغشود.
البته پمشنهاد مغشود بمشتر از حلقههای forبزای whileاستفاده پنمد .چرا په ایناار
به خوانایغ پد شما پمک مغپند.
1 Generators
فصل ششم :حلقهها در پایتون 237
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ترفندهای پایتون
این مولد همانند پرالس Repeaterعمرل مغپنرد و بره همران صرورت نترایج را در
خروجغ چاپ مغپند( .توجه داشته باشمد په برای توقف باید از Ctrl+Cاستفاده پنمد).
اما یک مولد در پایتون به چه صورت پار مغپند؟ مولدها از لحاظ ظراهری هماننرد
یک تابع نوشته مغشوند اما عملارد آنها متفاوت است .هنگامغ په یک مولد صدا زده
مغشود ،مقداری به سمت ما برگردانده نمغشود .بلاره یرک شرغء از مولرد مرورد نظرر
ساخته مغشود.
)'>>> repeater('Hey
><generator object repeater at 0x107bcdbf8
برای بدست آوردن مقداری په yieldمغشود باید از تابع nextدر پرایتون (هماننرد
آنچه در بخی قبل دیدیم) استفاده پنمم.
در واقع با استفاده از ،yieldمولد ما متوقف شده و مقداری را برگردانده و دوباره بره
پار خود ادامه مغدهد.
def repeater(value):
while True:
yield value
فصل ششم :حلقهها در پایتون 239
این قابلمت باعث مغشود تا مولدها بطور پامل بتوانند از پروتال iteratorدر پایتون
پشتمبانغ پنند .به هممن دلمل پمشنهاد مغشود ترا بزرای نوشرتن پالسهرایغ طروالنغ ،از
مولد ها بدین منظور استفاده پنمد.
پردیم .در واقع در مولدها نمز هممن عمل در پشت صحنه رخ مغدهد ،اما نمرازی نمسرت
تا ما آن را در پد خود به صورت دسرتغ بنویسرمم .هنگرامغ پره مقرداری برا اسرتفاده از
yieldاز یک مولد برگردانده نشود ،بطور خودپار روند آن متوقف مغشود.
def repeat_three_times(value):
yield value
yield value
yield value
در این مولد از whileاستفاده نشده و به صرورت بسرمار سراده تنهرا سره برار از yield
استفاده شده است .پس از فراخوانغ مولد ،هر پدام از yieldها طبق روال مقداری را بره
صدا زننده برمغگردانند .هنگامغ پره بره انتهرای مولرد برسرمم ،رونرد اجررای آن بطرور
خودپار متوقف خواهد شد.
این مولد پس از 3بار نمایی مقدار دریافتغ متوقف خواهد شد .ایرن عمرل در پشرت
صحنه با همان خطای StopIterationپنترل مغشود .برای اطممنران بره قطعره پرد زیرر
نگاهغ مغاندازیم.
پس از 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
اگر بخواهمم این پالس را با استفاده از یک مولد پماده سازی پنرمم ،مغتروانمم پرد
آن را به صورت زیر بنویسمم.
در قطعه پد باال به صورت عمدی از returnدر حلقهی whileخود استفاده پردیم
تا پس از تعداد max_repeatsخطای StopIterationرا دریافت پنرمم .در ادامره ایرن
242ترفندهای پایتون
مولد را به صورت ساده تر پماده سازی مغپنمم .اما ابتدا بمایمرد نگراهغ بره عملاررد ایرن
برنامه بمندازیم.
بدین صورت یک مولد ایزاد نمودیم په برا دریافرت تعرداد تاررار ،یرک مقردار را
نمایی مغدهد .برای نمایی مقدار دریافتغ از yieldاستفاده شده است و پرس از تعرداد
max_requestاز returnاستفاده شده است په دیگر چمزی نمایی داده نشده و حلقه
متوقف خواهد شد.
همانطور په گفته بودیم ،قصد داریم تا این مولرد را بره صرورتغ سراده ترر نمرز پمراده
سازی پنمم .مغدانمد په پایتون به صورت ضمنغ عبارت return Noneرا در انتهای هر
ترابع قررار مغدهررد .مرا از ایررن قابلمرت اسررتفاده پررده و مولررد خرود را برره صرورت زیررر
مغنویسمم.
قطعررره پرررد بررراال هماننرررد مولرررد قبلرررغ عمرررل مغپنرررد .مرررا از پرررالس 12خطرررغ
BoundedRepeaterبه یک مولد 3خطغ رسمدیم په دقمقا همان قابلمت را دارد.
استفاده از مولدها در برنامه نویسغ ،به پد نویسرغ شرما پمرک زیرادی مغپنرد و برا
استفاده از آنها مغتوانمد پدهایغ ساده تر و تممز تر پماده سازی پنمد.
فصل ششم :حلقهها در پایتون 243
این عبارت تک خطغ دقمقرا همران پراری را بررای مرا انزرام مغدهرد پره در مولرد
bounded_repeaterپماده سازی پرده بودیم .در زیر برای یادآوری این متد را دوباره
مغتوانمد ببمنمد.
البته توجه داشته باشمد ،زمانغ په یک مولد بردین صرورت سراخته مغشرود ،پرس از
یابار استفاده از آن ،دیگر نمغتوان از آن استفاده پررد .بنرابراین در برخرغ مروارد بهترر
است تا مولدها را به همان صورت قبل بطور ترابعغ یرا بره صرورت پرالس پمراده سرازی
نمایمم.
برخالف لمست ،ساخت یک مولد بدین صورت اشمایغ از عناصر به صرورت لمسرت
نمغسازد و تنها مقادیری را به صورت لحظهای و یابار مصرف ایزراد مغپنرد (هماننرد
مولدهایغ په به صورت تابع یا پالس در بخیهای قبل مغساختمم).
در صورتغ په بخواهمم محتویات این متغمرها را مشاهده پنمم ،خروجرغ زیرر بره مرا
نمایی داده مغشود.
>>> listcomp
]'['Hello', 'Hello', 'Hello
>>> genexpr
><generator object <genexpr> at 0x1036c3200
همانند بخی قبل ،برای دسترسغ به مقرادیر ایرن مولرد بایرد از ترابع nextدر پرایتون
استفاده پنمم.
)>>> next(genexpr
''Hello
)>>> next(genexpr
''Hello
)>>> next(genexpr
''Hello
)>>> next(genexpr
StopIteration
همچنمن مغتوانمد از تابع listروی یک مولد استفاده نمود ترا آن را بره یرک لمسرت
تبدیل پند .به مثال زیر توجه پنمد.
به صورت باال مغتوان به راحتغ یک مولد را بره لمسرت تبردیل نمرود .البتره پمشرنهاد
مغشود برای ساخت یک لمست در یک خرط از همران روش قبرل اسرتفاده پنمرد .اگرر
246ترفندهای پایتون
بخواهمم به طور پلغ به الگوی چگونگغ ساخت یک مولد در یک خط دست یابمم ،برا
پمغ بررسغ پدهای باال مغتوان الگویغ همانند زیر نوشت.
خط باال را مغتوان همانند یک تابع نمز برای ایزاد مولد به صرورت زیرر نوشرت پره
این دو دقمقا به یک صورت عمل مغپنند.
def generator():
for item in collection:
yield expression
بدین ترتمب مغتوان هر مولدی په به صرورت ترابع نوشرته شرده اسرت را برا پمرک
الگوی باال ،در یک خط پماده سازی نمود.
مولد باال مربع تمام اعداد زوج بمن 0ترا 9را شرامل مغشرود .همرانطور پره مشراهده
مغپنمد ،در شرط این مولد خاصمت زوج بودن اعداد مورد نظر بررسرغ شرده و تنهرا از
آنها استفاده مغشود.
حال مغتوانمم الگوی قبل را بروز رسانغ پرده و خاصمت بررسغ شرط و فملتر پردن
مقادیر را نمز در آن لحاظ پنمم.
الگوی باال دقمقا همانند الگوی ساخت یک مولد به صورت تابع است .همانند حالت
قبل ،این دو از لحاظ عملارد به یک صورت هستند.
def generator():
for item in collection:
if condition:
yield expression
همچنمن اگر بخواهمم مقادیر یک مولد را بره عنروان آرگومران بره یرک ترابع دهرمم،
مغتوانمم از نوشتن پرانتز برای آن صرف نظر پنمم تا پد ما زیبا تر و سراده ترر شرود .بره
مثال زیر توجه پنمد:
# Versus:
بدین صورت پد نویسغ ما ساده تر مغشود .توجه داشته باشمد پره مولرد هرا فضرای
حافظهی بسمار پمغ اشغال مغپننرد .چررا پره مقرادیر درون آن پرس از یابرار اسرتفاده
بالفاصله از بمن مغروند .بدین ترتمب برای بسماری از برنامههای په مغخواهمم بنویسمم،
استفاده از مولدها مغتواند مفمد واقع شود.
عبارت باال را مغتوان به صورت یک تابع مولدی همانند زیر نمز پماده سازی نمود.
for x in xs:
if cond1:
for y in ys:
if cond2:
...
for z in zs:
if condN:
yield expr
پمشنهاد مغشود به همک وجه چنمن حلقههایغ را به صورت یک عبرارت ترک خطرغ
ننویسمد .چرا په درک برنامرهی شرما بررای سرایر برنامره نویسران بسرمار سرخت بروده و
نگهداری چنمن پدهایغ بسمار دشوار است.
هممشه سعغ پنمد تا از عبارات تک خطغ برای موارد ساده استفاده پنمد .بهتر اسرت
در صورتغ په نماز است تا دو حلقهی تو در ترو در یرک مولرد ایزراد پنمرد ،از نوشرتن
عبارت تک خطغ برای ساخت مولد پرهمرز پررده و بزرای آن از روش ترابعغ اسرتفاده
فصل ششم :حلقهها در پایتون 249
نمایمد .بنابراین بهتر است تا برای ساخت مولدهای پمچمده از روش تابعغ یا ساخت یک
پالس بدین منظور پمک بگمرید.
در صورتغ په قصد دارید تا از شرطهای پمچمده در پنار حلقهها برای ساخت یرک
مولد استفاده پنمد ،بهتر است تا آن را به چندین زیر مولد پوچاتر تقسمم پرده و سپس
آنها را به یادیگر پموند دهمد .در بخی آینده با این روش بمشتر آشنا خواهمم شد.
1 Chain
250ترفندهای پایتون
در قطعه پد زیر دنبالهای از اعداد را از 1تا 8در یک مولد تابعغ ایزاد نمودیم.
def integers():
for i in range(1, 9):
yield i
حال از این مولد استفاده پررده و مقرادیر خروجرغ آن را برا اسرتفاده از ترابع listدر
پایتون ،به لمستغ از مقادیر تبدیل مغپنمم.
در ادامه قصد داریم تا مولدی دیگر بسازیم په این مولد دنبالهای از اعداد را گرفته و
مربع آنها را به ما برمغگرداند.
def squared(seq):
for i in seq:
yield i * i
این دو مولد مغتوانند به یاردیگر بره صرورت زنزمررهای وصرل شرده و دنبالرهای از
پردازشها را انزام دهند و یک خروجغ واحد را به ما نمایی دهند .تنها پافغ اسرت ترا
مولد integersرا به squaredپاس دهمم و خروجغ آنهرا را بره صرورت یرک لمسرت
مشاهده نمایمم .در قطعه پد زیر برای صحت عملارد این دو مولد به صورت زنزمرهای
این پار را انزام دادهایم.
همانطور په مشاهده مغپنمد ،لمست نمایی داده شده برابر با مربع اعداد 1تا 8است.
در واقع ما یک خط لوله 1برای پردازش پشت هم دو مولد ایزاد پردهایم .مفهروم خرط
1 pipeline
فصل ششم :حلقهها در پایتون 251
لوله در اینزا ،در سمستمهای Unixنمرز بارار بررده مغشرود پره چنردین پرردازش را بره
یارردیگر مرررتبط پرررده و خروجررغ هرپرردام را برره عنرروان ورودی دسررتور بعررد در نظررر
مغگمرند.
در ادامه قصد داریم تا یک مولد دیگر به خط لولهی پردازشهای خود اضافه پنرمم.
این مولد تمام مقادیر موجود در یک دنباله را منفغ مغنماید.
def negated(seq):
for i in seq:
yield -i
حال اگر این سه مولد را به صورت زنزمرهای به یادیگر متصل پنمم ،خروجغ زیرر
را خواهمم داشت.
توجه داشته باشمد په تنها یک مقدار در هر لحظه برای پردازش مغتواند در این خط
لوله قرار گمرد و همک دادهای بافر نمغشود .در مرحلهی اول مولد 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
تنها نقطه ضعف استفاده از این روش این است په آنها را نمغتوان با آرگومانهایغ
دیگر استفاده نمود .پس از یابار استفاده از این مولدها ،آنها پامالد از بمن رفتره و بررای
استفادهی مزدد از آنها باید دوباره این مولدها را ایزراد نمرایمم .بنرابراین توجره داشرته
باشمد په بسته به نماز خود در جای مناسب از چنمن مولدهایغ استفاده پنمد.
فصل هفتم
{ = 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ترفندهای پایتون
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برای
آنها استفاده شود تا از خطاهای احتمالغ نمز جلوگمری شود.
برای ایناه بتوانمم پلمد و مقرادیر ایرن دیاشرنری را مرترب پنرمم ،مغتروانمم از مترد
itemsروی این دیاشنری استفاده پرده و سپس از تابع sortedبرای مرتب سرازی آن
استفاده نمایمم.
))(>>> sorted(xs.items
])[('a', 4), ('b', 3), ('c', 2), ('d', 1
فصل هفتم :ترفندهایی برای کار با دیکشنریها 259
همانطور په مشاهده مغپنمد ،پلمدها و مقادیر آنها بره صرورت یرک تاپرل نمرایی
داده شدهاند و سپس تابع sortتمام آنها را در یک لمست قرار داده است.
به منظور مقایسهی دو تاپل ،تابع sortedاندیس صفرم هر پردام از آنهرا را مقایسره
مغپند (په در مثال باال همان پلمد دیاشرنری اسرت) .در صرورتغ پره هرر دو انردیس
صفرم برابر بودند ،به سراغ اندیس یام مغرود .در مثرال براال ،تمرام انردیسهای صرفرم
منحصر به فرد بوده و عملمات مرتب سازی به سادگغ انزام مغشود.
گاهغ اوقات ممان است قصد داشته باشرمم ترا یرک دیاشرنری را براسراس مقرادیر
پلمدهای آنها مرتب نمایمم .این عمل را برا اسرتفاده از آرگومران keyدر ترابع sorted
مغتوانمم انزام دهمم Key .در واقع یک تابع پایتون را از ما دریافت مغپند په برا توجره
به آن تابع ،مقادیر یک تاپل را مرتب مغنماید .توجره داشرته باشرمد پره اینزرا منظرور از
،keyپلمد موجود در دیاشنریها نمست؛ بلاه پلمدی است په تابع sortedبرا اسرتفاده
از آن عملمات مرتب سازی را انزام مغدهد.
فرض پنمد په همان دیاشنری مثال قبل را مغخرواهمم برر اسراس مقرادیر پلمردهای
موجود در آن مرتب نمایمم .بدین منظور از تابعغ به صورت lambdaاسرتفاده مرغپنمم
په برای ایناار از اندیس یام هر تاپل برای مرتب سازی استفاده مغنماید.
گاهغ اوقات برای استفاده از برخغ از توابع پمچمده ،استفاده از پتابخانهی operator
به خوانایغ بمشتر پد شما پمک مغپند .اما برای توابع سادهای ماننرد مثرال گفتره شرده،
استفاده از lambdaپمشنهاد مغشود.
همچنمن یاغ دیگر از مزیتهای استفاده از lambdaدر این است پره مغتوانمرد بره
طور پامل خروجغ آن را به صورت دلخواه مدیریت پنمد .برای مثال مغتوانمرد مرترب
سازی یک دیاشنری را با محاسبهی قدر مطلق مقادیر موجود در آن انزام دهمد.
در صورتغ په بخواهمرد خروجرغ شرما بررعاس نمرایی داده شرود (از بزرگترر بره
پوچاتر) ،تنها پافغ اسرت آرگومران reverseرا در ترابع sortedبرابرر برا Trueقررار
دهمد.
>>> sorted(xs.items(),
key=lambda x: x[1],
)reverse=True
])[('a', 4), ('b', 3), ('c', 2), ('d', 1
همانطور په پمی تر نمز اشاره شد ،بهتر است تا با برخغ از توابرع پلمردی در پرایتون
آشنا شوید .این توابع به پد نویسغ شما پمک شایانغ مغپنند و همچنمن برا اسرتفاده از
آنها مغتوانمد یک نو داده را به نوعغ دیگر نمز تبدیل پنمد.
مدیریت بمشتر به منظرور مرترب سرازی عناصرر ،از توابرع بره صرورت lambdaاسرتفاده
نمایمد.
مسلما برای بررسغ 3حالت مختلف ،این عبارت به اندازه پافغ مناسب است .اما اگر
تعداد حاالت مورد نماز برای بررسغ بمشتر باشد بایرد چره پرار پررد؟ اگرر بخرواهمم 10
حالت مختف را بررسغ پنمم ،پد ما بسمار پمچمده و طوالنغ خواهد شد.
یاغ از راهها برای رفع این مشال استفاده از دیاشنریها است .پایتون دارای توابعغ
به صورت درجه یک 1است؛ یعنغ آنها مغتوانند بره عنروان آرگومران بره توابرع دیگرر
داده منتقل شده ،به عنوان مقدار از توابع دیگر بازگردانده شروند ،بره متغمرهرا اختصرا
شده و حتغ در ساختارهای دادهای ذخمره شوند.
برای شفاف سازی بمشتر مثال صفحه بعد را در نظر بگمرید .در این قطعه پد یک تابع
تعریف نمودیم و سپس برای دسترسرغ بره آن در آینرده ،آن را در یرک لمسرت ذخمرره
مغپنمم.
برای فراخوانغ این تابع ،باید به اندیس لمست مرورد نظرر دسترسرغ پمردا پنرمم؛ و در
ادامه مقادیری را به عنوان آرگومان به تابع خود انتقال دهمم.
حال با باارگمری این ایده قصد داریم تا بالکهرای if..elseطروالنغ خرود را بهمنره
پنمم .در ادامه یک دیاشنری تعریف پرده و سرپس بررای پلمردهای آن ،تروابعغ را بره
عنوان مقدار در نظر مغگمریم .به مثال زیر توجه پنمد.
{ = >>> func_dict
'cond_a': handle_a,
'cond_b': handle_b
}
در مثال باال handle_a ،و handle_bدو تابع هستند په هر پدام برای یک پلمد در
نظر گرفته شدهاند .حال بزای اسرتفاده از ،if/elseمقردار پلمردی را در ایرن دیاشرنری
جست و جو پرده و سپس تابع مربوط به آن را فراخوانغ مغپنمم تا عملمات مرورد نظرر
برای حالتغ په قصد داریم بررسغ شود ،انزام گمرد.
در صورتغ په ،condدر دیاشنری ما وجود داشته باشد ،تابع مربوط به آن صدا زده
خواهد شد .اما اگر این condدر دیاشنری وجود نداشته باشد ،خطای KeyErrorبه مرا
برگشت داده مغشود .این خطای KeyErrorدر واقع همان بالک elseما خواهد بود.
فصل هفتم :ترفندهایی برای کار با دیکشنریها 263
برررای رفررع ایررن مشررال از متررد getروی دیاشررنری خررود اسررتفاده مررغپنمم (برررای
اطالعات بمشتر به بخی 7.1مراجعه پنمد) .برا اسرتفاده از ایرن مترد ،ترابعغ را بره عنروان
مقدار پمی فرض (در صورت عدم وجود پلمد در دیاشنری) نمز به عنوان آرگومان بره
آن پاس مغدهمم .در صورتغ په پلمد موجود پمدا نشود ،این تابع فراخوانغ مغشود.
قطعه پد باال نمز همانند مثال قبل عمل مغپند .تنها با ایرن تفراوت پره ترابعغ نمرز بره
عنوان مقدار پمی فرض بررای معرادل سرازی برالک elseدر نظرر گرفتره شرده اسرت و
همچنمن از خطای KeyErrorنمز جلوگمری شده است.
بمایمد پمغ این حالت را پمچمده تر پرده و مثالغ پامل تر را بررسغ نمرایمم .پرس از
خواندن مثال زیر ،دیگر نوشتن هرگونه عبارت if..elif..elseبه صورت دیاشنری بررای
شما ساده خواهد شد.
ابتدا تابعغ را به صورت if..elif..elseپماده سازی مغپنمم .ایرن ترابع مقرداری را بره
عنوان آپاد دریافت مغنماید په مشخص پنندهی نو عملمات مورد نظر است (جمرع،
تفریق ،ضرب و تقسمم)؛ و دو مقدار دریافتغ دیگر با توجه به عملمات مورد نظر جمع یرا
ضرب یا … خواهند شد.
حال از تابع dispatch_ifپه در باال نوشتمم مغتوانمم به صورت زیر استفاده پنمم.
توجه داشته باشمد زمرانغ پره از unknownبره عنروان آپارد بررای ترابع مرورد نظرر
استفاده مغپنمم ،مقدار Noneبه ما نمرایی داده مغشرود .چررا پره پرایتون بره صرورت
ضمنغ در انتهای تمام توابع عبارت return Noneرا مغنویسد.
حال قصد داریم تابع dispatch_ifرا تغممر داده تا با استفاده از دیاشنری ،شررایط و
حاالت مختلف را بررسغ نمایمم .این تابع را dispatch_dictنامگذاری مغپنمم.
این تابع همانند تابع قبل عمل پرده و دقمقا همان خروجغ را به ما برمغگرداند.
راههای بسماری وجود دارد تا بتوان تابع dispatch_dictرا باز هم بهبود بخشمد.
هر زمان په ما این تابع را فراخوانغ مغپنمم ،یک دیاشنری به همرراه تعردادی ترابع
lambdaایزاد مغشود په از لحاظ عملارد بسمار بد اسرت .بررای بهبرود عملاررد ایرن
تابع ،بهتر است تا دیاشنری مورد نظر تنها یابار به صرورت ابرت ( )constantتعریرف
فصل هفتم :ترفندهایی برای کار با دیکشنریها 265
شود و زمانغ په تابع فراخوانغ مغشود ،به آن دیاشنری اشاره گردد .بدین ترتمب دیگر
نمازی به ساخت یک دیاشنری پس از هربار فراخوانغ تابع نمغباشد.
همچنمن برای استفاده از عملگرهای سادهای مانند جمع یرا ضررب ،بهترر اسرت ترا از
توابع داخلغ پایتون و پتابخانهی ( operatorبزرای ترابع )lambdaاسرتفاده شرود .ایرن
پتابخانه اپثر عملگرهای مورد نماز را برای ما فراهم پرده است .ماننرد ،operator.add
operator.mulو … .اما در مثال باال ،دلمل استفاده از lambdaتنها نشان دادن قابلمت
پماده سازی یک دیاشنری برای حاالت مختلف و انزام یاسری عملمات بوده است.
بدین ترتمرب مغتروانمم بسرماری از قطعره پردهای خرود را پره برا عبرارات طروالنغ
if..elif..elseنوشته شدهاند ،بدین صورت بازنویسغ نمایمم .البته توجه داشته باشمد په از
این تانمک در همهی شرایط استفاده نانمد .عبارات پوتاه if..elseبهتر است تا به همان
صورت نوشته شوند.
باعث مغشود تا نحوهی پارپرد مفسر 1پایتون را بهتر درک نمرایمم .بره قطعره پرد زیرر
توجه پنمد.
ابتدا نگاهغ دقمق تر به پد باال بمندازید .به نظرتان با نوشتن پد باال ،دیاشنری مرورد
نظر به هممن صورت ساخته خواهد شد؟
مفسر پایتون قطعه پد باال را به صرورت زیرر تفسرمر پررده و خروجرغ زیرر را بره مرا
نمایی مغدهد.
من هم برای اولمن بار با مشاهدهی این خروجغ متعزب شدم .اما در ادامه برا نگراهغ
دقمق تر به آن ،گام به گام پمی خواهمم رفت تا دریرابمم ایرن خروجرغ بره چره صرورت
حاصل شده است.
هنگامغ په مفسر پایتون دیاشنری نوشته شدهی ما را پردازش مغنمایرد ،ابتردا یرک
شغء از دیاشنری ساخته مغشود و سپس پلمدها و مقادیر آنها بره ترتمرب وارد شرده و
درون آن قرار مغگمرد .بنابراین اگر بخواهمم دقمق تر به نحوهی ساخت ایرن دیاشرنری
نگاه پنمم ،مراحل ساخت آن به صورت زیر خواهد بود.
دادهایم را برابرر در واقع مفسر پایتون تمام پلمدهایغ په به دیاشنری خود اختصا
با یادیگر مغداند.
1 interpreter
فصل هفتم :ترفندهایی برای کار با دیکشنریها 267
طبمعتا عدد 1با 1.0برابر است .اما چرا پایتون Trueرا نمز برابر با 1مغدانرد؟ پرس از
بررسغ داپمومنت زبان پایتون ،متوجه خواهمم شد په boolدر واقع زیر پالسرغ از int
است .طبق آنچه په در داپمومنت رسمغ پایتون آمده است:
این بدان معناست په شما حتغ مغتوانمد از boolها به عنوان اندیس یرک لمسرت یرا
تاپل نمز برای فراخوانغ عنصری از آنها نمز استفاده نمایمد.
اما به هر حال ایناار به همک وجره پمشرنهاد نمغشرود .چررا پره خوانرایغ پرد شرما را
سخت مغپند.
حال از آنزا په پایتون مقدار Trueرا برا 1و 1.0برابرر مغدانرد ،در هنگرام سراخت
دیاشنری ،هر بار مقدار این پلمد با مقدار جدید جایگزین مغشود .به هممن دلمل اسرت
په در انتها ،تنها یک پلمد با نام Trueوجود داشته و مقدار آن آخررین مقرداری اسرت
په به 1.0داده شده است؛ یعنغ . maybe
حال سؤالغ په پمی مغآید این است په چرا تنها مقردار پلمرد تغممرر پررده و خرود
پلمد بروز نمغشود و برابر با 1.0نمغباشد؟
دلمل این امر آن است په در واقع مفسر پایتون ،خود پلمرد را همچگراه برروز رسرانغ
نمغپند.
268ترفندهای پایتون
class AlwaysEquals:
def __eq__(self, other):
return True
def __hash__(self):
)return id(self
متد __ __eqدر این پالس هممشه مقدار Trueرا برای مرا برمغگردانرد .یعنرغ در
صورت مقایسهی این پالس با هر شغء دیگری ،مقدار Trueبه ما نمایی داده مغشود.
در مفسر پایتون ،تابع idآدرس شرغء موجرود در حافظره را بره نمرایی مغدهرد پره
هممشه منحصر به فرد است.
با استفاده از این پالس مغتوانمم اشمایغ ایزاد نمایمم په وانمود مغپنند با هر شرغء
دیگری برابر بوده اما مقدار هی متفاوتغ دارند .حرال ایرن پرالس بره مرا ایرن اجرازه را
مغدهد تا ببمنمم پره آیرا پلمردهای موجرود در یرک دیاشرنری ،تنهرا در صرورت برابرر
بودنشان با یادیگر روی یادیگر نوشته مغشوند ( )overwriteیا خمر.
270ترفندهای پایتون
همانطور په در مثال زیر مشاهده مغپنمد ،دیاشنری این دو پلمد را با یادیگر برابر
نمغداند.
حال قصد داریم تا مترد __ __hashرا تغممرر دهرمم و هممشره یرک مقردار را از آن
برگردانمم تا ببمنمم آیا بدین صورت مغشود یک پلمد را روی پلمدی دیگرر نوشرت یرا
خمر.
class SameHash:
def __hash__(self):
return 1
در ادامه مشاهده مغپنمم په نمونههای پالس SameHashبا یادیگر برابرر نمسرتند
اما هر دوی آنها یک مقدار را به عنوان مقدار هی به ما بر مغگردانند.
)(>>> a = SameHash
)(>>> b = SameHash
>>> a == b
False
)>>> hash(a), hash(b
)(1, 1
حال اگر از نمونههای این پالس به عنوان پلمدهای یک دیاشرنری اسرتفاده نمرایمم
چه اتفاقغ رخ خواهد داد؟
همانطور په مشاهده مغ پنمد ،در مثال باال نمز پلمدها روی یادیگر نوشته نشدهاند.
فصل هفتم :ترفندهایی برای کار با دیکشنریها 271
دیاشنریها برابری را بررسغ پرده و مقادیر هیها را با یادیگر مقایسه مغپنند ترا
تشخمص دهند په دو پلمد با یادیگر برابر هستند یا خمر .بمایمد تا بره جمرع بنردی آنچره
په تا بدین جا فهممدهایم بپردازیم:
برره طررور پلررغ دیاشررنری }” {True: ‘yes’, 1: “no”, 1.0: “maybeتبرردیل برره
دیاشنری }” {True: “maybeخواهد شد .چراپه پلمردهای 1 ،Trueو 1.0هرر سره برا
یادیگر برابر بوده و دارای مقدار هی یاسانغ هستند.
حال دیگر مشاهدهی خروجغ زیر به هنگام ساخت این دیاشنری برای شما رمر قابل
انتظار نخواهد بود.
اگر درک موضوعات مطرح شده در این بخی برای شما پمغ دشوار اسرت ،سرعغ
پنمد نمونه پدهای موجود را خودتان در مفسر پایتون بنویسمد تا بتوانمرد ایرن مفراهمم را
بهتر فرا گمرید .بدین صورت دانی شما از نحوهی پرارپرد زبران پرایتون بمشرتر خواهرد
شد.
حال قصد داریم تا یک دیاشنری برا نرام zsبسرازیم پره تمرام پلمردها و مقرادیر دو
دیاشنری xsو ysدرون آن وجود داشته باشد .اگر به مثال براال بمشرتر توجره پنمرد ،پرغ
خواهمد برد په رشتهی bبه عنوان پلمد در هر دو دیاشرنری وجرود دارد .بنرابراین نمراز
است تا این مغایرت 1در دو دیاشنری را نمز به نوعغ رفع نمایمم.
یاغ از روشهای مرسوم برای ادرام چنمن دیاشنریهایغ در پایتون ،استفاده از متد
updateموجود در دیاشنری است.
1 conflict
فصل هفتم :ترفندهایی برای کار با دیکشنریها 273
در واقع متد updateتمام عناصر موجود (پلمدها و مقادیرشان) در دیاشنری سمت
راست را در دیاشنری سمت چپ مغنویسرد و در صرورتغ پره پلمردی در دیاشرنری
سمت چپ وجود داشته باشد ،با مقدار جدید از پلمد سمت راست جایگزین مغشود.
>>> zs
}>>> {'c': 4, 'a': 1, 'b': 3
همانطور په مشاهده مغپنمد ،از آنزا په آخررین مترد updateروی دیاشرنری ys
انزام شده است ،پلمد bبا مقدار موجود در ysبرای دیاشنری zsقرار داده شده اسرت.
شما مغتوانمد در ادامه نمز باز هم از این متد استفاده پرده تا تعداد دیاشنریهای بمشتری
را درون zsادرام نمایمد.
یاغ دیگر از ترفندهای موجود برای ایزاد یرک دیاشرنری برا ادررام دو دیاشرنری
دیگر ،استفاده از تابع dictبه همراه عملگر ** در پایتون است.
البته توجه داشته باشمد په این روش برای ادرام تنها دو دیاشنری باار برده مغشرود
نمود1. و نمغتوان در یک خط بمی از دو دیاشنری را با یادیگر ادرام
1برای ادرام بمی از دو دیاشنری باید دوباره این دستور را برای دیاشنریهای جدید بنویسمد.
274ترفندهای پایتون
همچنمن در نسخهی پایتون 3.5به بعد ،شما مغتوانمد از عملگر ** به نوعغ دیگر نمز
برای ادرام دیاشنریها پمک بگمرید .این روش یاغ از روشهای بهمنره بررای اینارار
است و قدرت برنامه نویسغ شما را افزایی مغدهد .از این عملگرر مغتروان بره صرورت
زیر برای ادرام تعداد دلخواهغ از دیاشنریها استفاده نمود.
عملارد این روش دقمقا برابر با روشهای قبل است .در قطعه پد باال نمرز دیاشرنری
سمت راست اولویت را بدسرت گرفتره و در صرورتغ پره پلمردی تارراری در ایرن دو
دیاشنری وجود داشته باشد ،مقدار این پلمرد برابرر برا مقردار موجرود در دیاشرنری ys
خواهد بود .خروجغ حاصل از دستور باال در دیاشنری zsقرار داشته په به صورت زیر
است.
>>> zs
}>>> {'c': 4, 'a': 1, 'b': 3
استفاده از این سمنتاس جدید ،پار را برای ما بسمار آسان پرده است .برا اسرتفاده از
این روش دیگر نمازی به نوشتن چندین متد updateبرای ادررام برمی از دو دیاشرنری
نمغباشد .بنابراین عملگر ** در پایتون 3بسمار پاربردی بوده و پار را برای ما ساده ترر
مغپند.
خوشبختانه روشهای دیگری نمرز بزرای اسرتفاده از ترابع strوجرود دارد پره بررای
نمایی زیباتر یک دیاشنری باار برده مغشود .یاغ از این روشها ،اسرتفاده از مراژول
jsonدر پایتون است .با پمک json.dumpsمغتوان دیاشنریها را به صورتغ زیبراتر
نمایی داد.
خروجغ باال بسمار زیبا تر از حالت قبرل برا ترو رفتگغهرای مناسرب بروده و همچنرمن
پلمدهای دیاشنری نمز در آن به ترتمب مرتب شدهاند.
1 indention
276ترفندهای پایتون
درحالغ په دیاشنریها بدین صورت بسمار زیبا و قابل خوانا نمایی داده مغشروند،
اما این روشغ پامل و بدون نقص نمست .نمایی دیاشنریها با استفاده از مراژول ،json
تنها برای دیاشنریهایغ مو ر است په مغتوانند با اسرتفاده از آن سرریال 1شروند .انروا
دادههای قابل پشتمبانغ در آن عبارتند از:
بنابراین هنگامغ په بخواهمد یک دیاشنری را چاپ پنمد پره شرامل یارغ از انروا
داده بزز موارد باال باشد(برای مثال یک تابع) ،با خطا مواجه خواهمد شد.
حال بهتر است روشغ دیگر را مورد بررسغ قرار دهمم ترا دیگرر برا چنرمن مشراالتغ
مواجه نشویم .یاغ از روشهای قدیمغ و مرسوم برای نمرایی زیبرای اشرما در پرایتون،
استفاده از ماژول pprintاست .به مثال زیر توجه پنمد.
همانطور پره مشراهده مغپنمرد ،ایرن مراژول توانرایغ نمرایی دادهی setرا داشرته و
همچنمن پلمدها را نمز به ترتمب نمرایی مغدهرد .در مقایسره برا روش قردیمغ و سرنتغ،
خروجغ حاصل از این روش زیباتر بوده و قابلمت خوانایغ باالتری دارد.
1 serialized
فصل هفتم :ترفندهایی برای کار با دیکشنریها 277
فصل هشتم
در مثال باال ،ابتدا پتابخانرهی 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و شرط ifدر یک خط ،تنها مرواردی
را از خروجغ دستور ) dir(datetimeاستخراج پرردیم پره در آنهرا نرام dateوجرود
دارد .توجه داشته باشمد په استفاده از متد lowerبدین منظور است په جسرت و جروی
ما حساس به حروف پوچک و بزرگ نباشد.
گاهغ اوقات دریافت تنها لمستغ از ویژگغهای موجود در یک ماژول ممارن اسرت
پماغ به ما ناند .بنابراین نماز اسرت ترا اطالعرات بمشرتری در مرورد هرپردام از آنهرا
بدست آورده و با نحوهی پار با آنها آشنا شویم.
با استفاده از تابع helpدر پرایتون مغتروانمم اطالعرات جرامعغ در مرورد تمرام اشرماء
موجود در پایتون بدست آوریم.
)>>> help(datetime
با اجرای دستور باال در مفسر پایتون ،اطالعاتغ در مرورد مراژول datetimeبره شرما
نمایی داده مغشود.
NAME
datetime - Fast implementation of the datetime
type.
CLASSES
builtins.object
date
datetime
time
نمایمد .این تابع در صورت وجود docstringدر پالس شما ،آن را نمایی خواهرد داد.
در ادامه سایر موارد استفاده از این تابع را مغتوانمد مشاهده پنمد.
)>>> help(datetime.date
)>>> help(datetime.date.fromtimestamp
)>>> help(dir
طبمعتررا بررا پمررک موتورهررای جسررت و جررویغ ماننررد Googleو سررایتهایغ ماننررد
،Stackoverflowاطالعات جامع ترری نسربت بره اسرتفاده از تروابعغ ماننرد dirو help
مغتوانمد بدست آورید .اما این توابع نمز گراهغ اوقرات بررای بدسرت آوردن اطالعراتغ
مختصر و زمانغ په دسترسغ به اینترنت ندارید ،مغتوانند مفمد واقع شوند.
زبان برنامه نویسغ پایتون دارای یرک سمسرتم یاپارچره سرازی بسرتهها اسرت پره برا
استفاده از آن مغتوانمد بستههای مرورد نمراز خرود را پره بره پمرک ابرزار pipنصرب و
مدیریت پنمد .یاغ از مشاالت موجود در هنگام نصرب یرک بسرته ،1ا رر گرذاری آن
روی محمط globalپایتون است .بدین معنا په بستهی مورد نظر روی پل سمسرتم عامرل
نصب مغشود.
مشال زمانغ رخ مغدهد په چندین پروژه در سمستم خود داشته باشمم په هر پردام
از آنها از نسخهی متفاوتغ از یک پامج اسرتفاده مغپننرد .بررای مثرال یرک پرروژه برا
Django 2.2پار مغپند؛ در صورتغ په پروژهی دیگر نماز به Django 3.0دارد.
1 package
فصل هشتم :تکنیکهایی برای بهره وری بیشتر از زبان پایتون 285
هنگررامغ پرره یررک بسررته را برره صررورت globalروی سمسررتم نصررب مغپنمررد ،تنهررا
مغتوانمد یک نسخه از هر بسته روی سمستم خود داشرته باشرمد پره مغتوانرد مشراالتغ
برای شما در آینده به وجود آورد.
حتغ ممان است پروژههایغ روی سمستم خود داشته باشمد په هرپردام برا نسرخهی
مختلفغ از پایتون پار مغپنند .برای مثال برنامهای قدیمغ روی سمستم شما وجرود دارد
په هنوز به توسعهی آن مشغولمد و نماز به استفاده از پایتون 2دارید .در حرالغ پره سرایر
پروژههای شما از پایتون 3استفاده مغپنند .یا حتغ شراید پرروژهای نمراز بره پرایتون 3.5
داشته و پروژهای دیگر به پایتون 3.7نماز داشته باشد.
عالوه بر این موارد ،نصب یک بسته به صورت globalممان است مشاالتغ امنمتغ
روی سمستم عامل ایزاد پند .چرا په برای نصب یک بسته به این صورت ،نماز است ترا
شما دسترسغ سطح باال ( rootیا )adminدر سمستم داشته باشرمد؛ و زمرانغ پره دسرتور
pip installرا اجرا مغپنمد ،پامج شما از اینترنت دانلود شده و نصب مغشود.
ابتدا بمایمد تا ببمنمم محمط پایتون globalمرا در چره مسرمری قررار دارد .بررای اینارار
مغتوانمم از دستور whichدر سمستم عاملهای Linuxیا macOSاستفاده پرده تا مسرمر
مدیر بستهی pip 1را پمدا پنمم.
در اپثر مواقع پمشنهاد مغشود تا محمط ایزولهی خود را در همان مسمر پروژهی خود
بسازید .برای ساخت یک virtualenvاز دستور زیر استفاده مغشود.
با اجرای این دستور ،یک مسمر با نام venvبرای شما ساخته مغشود په ایرن محرمط
ایزوله از پایتون 3استفاده مغپند.
$ ls venv/
bin include lib pyvenv.cfg
حال اگر دوباره از دستور whichبرای پمدا پردن نسخهی فعرال pipاسرتفاده پنرمم،
دوباره همان خروجغ globalمانند حالت قبل به ما نمایی داده مغشود.
این خروجغ بدان معناست په هنوز هم اگر پامزغ را با pipنصرب پنرمم ،آن بسرته
به صورت globalروی سمستم نصب مغشود .ما یک گام دیگر برای استفاده از محرمط
ایزوله ی خود فاصله داریم و آن هرم فعرال سرازی آن اسرت .بررای فعرال سرازی محرمط
virtualenvپه دقایقغ پمی ایزاد پردیم نماز است تا دستور زیر را وارد پنمم.
با اجرای فایل activateدر ،virtualenvمحمط مزازی ما فعال شرده و از ایرن پرس
تمام دستورات پایتونغِ ما تنها روی همرمن محرمط ترا مر مغگذارنرد .بررای تصردیق ایرن
موضو مغتوانمم دوباره از دستور whichبرای pipاستفاده پنمم.
همانطور په مشاهده مغپنمد ،از این پس با اجرای دستور ،pipنسرخهی موجرود در
virtualenvفراخروانغ مغشرود و دیگرر بسرتهای بره صرورت globalدر سمسرتم نصررب
نخواهد شد .هممن حالت برای اجرای دستور pythonنمز صردق مغپنرد .برار دیگرر برا
پمک دستور whichقصد داریم بفهممم پره آیرا در هنگرام اجررای مفسرر ،pythonاز
محمط مزازی ما استفاده مغشود یا خمر.
البته توجه داشته باشمد په محمط ما در حال حاضر پامالد خالغ بوده و بسرتهای بررای
محمط پایتونغ ما نصب نشده است .با اجرای دستور زیر مغتوانمم مشاهده پنمم پره تنهرا
بستههای پایهای و پمی فرض در محمط ایزوله شدهی ما وجود دارد.
اگر در این حالت یک فایل پایتونغ (فایلغ با پسوند )pyرا اجرا پنمم یا برای هر پار
دیگری از دستور pythonاسرتفاده پنرمم ،از نسرخهی پرایتون موجرود در همرمن محرمط
مزازی و بستههای نصب شده برای آن استفاده مغشود.
اما چگونه مغتوان از این محمط خارج شد؟ همانند دستور ،activateدستوری با نرام
deactivateنمز وجود دارد په با اجرای آن مغتوانمم از محمط venvخارج شویم.
(venv) $ deactivate
$ which pip3
/usr/local/bin
به طور پلغ استفاده از virtual environmentباعث مغشود تا بهتر بتوانمد بستههای
پایتونغ خود را با توجه به نمازتان در هر پروژه به صورت مززا مردیریت پنمرد .بنرابراین
هممشه پمشنهاد مغشود تا برای تمام پروژههایتران از یرک محرمط مزرازی جردا اسرتفاده
پنمد.
فصل هشتم :تکنیکهایی برای بهره وری بیشتر از زبان پایتون 289
همچنمن توجه داشته باشمد په برای پروژههای خود یرک فایرل requirements.txt
ایزاد پرده و تمام بستههای نصب شده برای پروژهی خود را در آن ذپر پنمد .برای این
پار مغتوانمد از دستور زیر استفاده پنمد.
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مشخص است ،آنها مقادیر ابتغ هسرتند و
در برنامه تغممر نمغپنند .این مقادیر به منظور صرفه جرویغ در مصررف حافظره ،جردا از
codeو varnamesنگهداری شدهاند.
بنابراین زبان پایتون بزای تارار مقادیر ابت در ،co_codeآنها را در ماانغ مزرزا
نگهداری مغپند؛ و از این پس co_codeمغتواند با جست و جو در فهرست ابتهرا،
آنها را بمابد و در برنامه جای دهد .هممن عمل برای مقادیر co_varnamesنمرز صردق
مغپند.
هنوز هم با مشاهدهی خروجغ co_codeناتهی خاصغ دسرتگمرمان نمغشرود .ایرن
خروجغ در واقع زبانغ برای ارتباط با ماشمن مزازی پایتون بوده و قابل خوانردن نمسرت.
توسعه دهندگان CPythonایرن زبران را درک مغپننرد .بنرابراین آنهرا ابرزاری برا نرام
disassemblerبرای ما فراهم پردهاند په با پمرک آن مغتروانمم ایرن بایرت پردها را
بهتر درک پنمم.
دیس اسمبلر ( )disassemblerدر پایتون در ماژولغ با نام disقرار دارد .بنرابراین بره
راحتغ مغتوانمم از این ماژول و تابع dis.disموجود در آن به منظرور درک بهترر بایرت
پدهای تابعغ په نوشتهایم استفاده پنمم.
در واقع این پتابخانه دستورالعملهای موجود را به بخیهای مختلفغ تقسمم پرده و
داده است .همچنمن مغتوانمد مشاهده پنمد په به هرپدام یک آپاد مشخص اختصا
مراجررع مربرروط برره ابتهررا و متغمرهررا بررا بایررت پرردها ترپمررب شررده و co_constsو
co_varnamesبه صورتغ واضح به ما نشان داده شده است.
292ترفندهای پایتون
با نگاهغ دقمق تر به آپادهای ارایه شرده ،مغتروانمم دریرابمم پره CPythonچگونره
دستورات نوشته شده در تابع greetرا اجرا مغپند .در واقع CPythonابتدا مقدار ابت
) (‘Hello’,در اندیس 1را دریافت پرده و آن را در یک پشته قرار مغدهد .سپس متغمر
nameرا دریافت پرده و آن را نمز به پشته اضافه مغپند.
ساختار دادهی پشته ( )stackبرای فضرای ذخمرره سرازی درونرغ در ماشرمن مزرازی
پایتون بسمار مورد استفاده قرار مغگمرد .پالسهای متنوعغ از ماشمن مزازی در پرایتون
وجود دارد په یاغ از آنها ماشمن پشرته اسرت .ماشرمن مزرازی CPythonنمونرهای از
پماده سازی یک ماشمن مزازی است .ما در این بخی وارد جزیمات مربوط به این مسائل
نخواهمم شد .خواندن تئوریهای مربوط به ماشمن مزازی بسمار مفصل بوده پره در ایرن
پتاب نمغگنزد.
آنچه په در مورد پشته به عنوان یک ساختار داده جالب اسرت ،پشرتمبانغ آن تنهرا از
دو عمل pushو popاست .عمل pushمقداری را به باالی یرک پشرته اضرافه پررده و
عمل popآخرین مقدار اضافه شده بره براالی پشرته را برمغگردانرد .بررخالف سراختار
دادهی آرایه ،اماان دسترسغ به سایر عناصر موجود پایمن تر از عنصر روی پشرته وجرود
ندارد.
فرض مغپنمم په در ابتدا پشتهی موجود خالغ است و سپس دو آپاد ابتدایغ اجررا
مغشود .پس از اجرای آنها ،پشتهی ما همانند زیر مغشود.
'!' 0:
'1: 'Hello, Guido
حررال دوبرراره دسررتور آپاررد BINARY_ADDاجرررا شررده و دو مقرردار روی پشررته برره
یادیگر متصل مغشود په حاصل آن رشتهی نهایغ ما خواهد بود.