فصل ۱۶
کلاس ها و توابع
در این مرحله که نحوه تعریف داده جدید را یاد گرفته ایم، مرحله بعد تعریف توابعی است که پارامتر ورودی و خروجی آنها اشیایی می باشد که توسط برنامه نویس تعریف شده است.
Time
یکی از انواع داده ها که توسط کاربر تعریف می شود Time می باشد که زمان را ثبت می کند. برای استفاده از این نوع داده، کلاسی از جنس نوع داده Time تعریف می کنیم. نحوه تعریف کلاس به صورت زیر می باشد:
Class Time
attributes: hour, minute, second
می توان کلاسی از نوع داده Time تعریف کرد و ويژگی های آن را مقدار دهی کرد.
time = Time()\ time.hour = 11\ time.minute = 59\ time.second = 30\
نمودار حالت برای شیء Time در شکل ۱۶.۱ نمایش داده شده است.
به عنوان تمرین، تابعی به نام print_time بنویسید که شیء Time را به عنوان ورودی دریافت کند و زمان را به فرمت hour:minute:second در خروجی نمایش دهد. توجه: '%.2d' عدد را به صورت صحیح تا دو رقم نمایش می دهد.
تمرین، تابعی به نام is_after بنویسید که دو آرگومان ورودی از جنس شیء Time دریافت کند به نام t1 و t2، خروجی تابع در صورتیکه t1 بعد از t2 باشد True و در غیر اینصورت خروجی False است.
توابع خالص
در بخش های بعدی، توابعی را خواهیم نوشت که مقادیر زمان را با هم جمع میکنند. دو نوع تابع را نشان می دهند: توابع خالص و تغییر یافته. همچنین روند طراحی نمونه یا بسته را نشان می دهند که یک راه مقابله با برنامه های پیچیده، شکستن برنامه به بسته های ساده تر است. در ادامه نمونه ساده add_time را نشان می دهیم:
def add_time(t1, t2):\ sum = Time()
sum.hour = t1.hour + t2.hour \ sum.minute = t1.minute + t2.minute\ sum.second = t1.second + t2.second\ return sum\
این تابع دو آرگومان ورودی از نوع شیء Time از ورودی دریافت کرده و sum را از نوع شیء Time تعریف میکند که مقادیر ویژگی ها آن برابر با مجموع مقادیر ویژگی های آرگومان های ورودی تابع است. این تابع خالص است از این جهت که شیء ورودی که به عنوان آرگومان ورودی تابع در نظر گرفته می شود مقادیر ویژگی هایش تغییر نمی کند.
برای تست تابع نوشته شده دو شیء از نوع Time تعریف می شود به نام های start و duration که به ترتیب زمان شروع فیلم و مدت زمان پخش فیلم را نشان می دهند. و ویژگی های هر دو شیء به صورت زیر مقدار دهی می شوند.
>>> start = Time()\ >>> start.hour = 9\ >>> start.minute = 45\ >>> start.second = 0\
>>> duration = Time()\ >>> duration.hour = 1\ >>> duration.minute = 35\ >>> duration.second = 0\ \
تابع add_time زمان اتمام فیلم را محاسبه میکند.
>>> done = add_time(start, duration)\ >>> print_time(done)\ 10:80:00\
نتیجه چیزی که ما انتظار داریم نیست به دلیل اینکه ۸۰ برای ثانیه بی معنی است. دلیل این اتفاق نبود قوانینی است که منجر به محاسبه درست زمان می شود. برای نشان دادن زمان درست در تابع تغییراتی به صورت زیر اعمال میکنیم.
def add_time(t1, t2):\ sum = Time()
sum.hour = t1.hour + t2.hour\ sum.minute = t1.minute + t2.minute\ sum.second = t1.second + t2.second\
if sum.second >= 60:\ sum.second -= 60
sum.minute += 1
if sum.minute >= 60:\ sum.minute -= 60
sum.hour += 1
return sum
تابع نوشته شده درست زمان را محاسبه می کند در ادامه جایگزین کوتاه تری برای این تابع خواهیم داشت.
تغییر یافته
در برخی موارد با توجه به تعریف مساله نیاز به توابعی داریم که مقادیر ویژگی های آرگومان های ورودی در صورت نیاز تغییر میکند. به این توابع تغییر یافته می گویند. در ادامه increment نمونه ای از modifier ها می باشد را نشان میدهیم.
def increment(time, seconds):\ time.second += seconds\
if time.second >= 60:\ time.second -= 60\ time.minute += 1\
if time.minute >= 60:\ time.minute -= 60\ time.hour += 1\
تابع نوشته شده درست است ولی اگر مقدار ثانیه خیلی بزرگتر از ۶۰ باشد چه اتفاقی می افتد؟
یک راه حل استفاده از شرط if و حلقه است. ولی با استفاده از حلقه کارایی تابع کم می شود. به عنوان تمرین در این حالت تابع را بدون استفاده از حلقه بنویسید.
هر چیزی که بتوان با modifier ها نوشت را میتوان با تابع خالص نوشت. در واقع، برخی از زبان های برنامه نویسی فقط اجازه استفاده از توابع خالص را می دهند. شواهدی وجود دارد حاکی از این موضوع که توسعه چنین برنامه های سریع تر می باشد و همچنین میزان خطا در این برنامه ها کمتر است. اما modifier ها راحت تر هستند و همچینن کارایی برنامه هایی که صرفا از توابع خالص استفاده می کنند کمتر است.
به صورت کلی، بهتر است از توابع خالص در برنامه نویسی استفاده شود مگر اینکه یک مزیت قانع کننده ای برای استفاده از modifier ها وجود داشته باشد. این رویکرد مدل برنامه نویسی تابعی نام دارد.
به عنوان تمرین تابع خالص برای increment بنویسید.
نمونه سازی در مقابل برنامه ریزی
با اینجا برای برنامه نویسی از نمونه سازی استفاده شد. در نمونه سازی برای هر بخش ساده یک نمونه ایجاد می شود که عملیات مشخصی را انجام می دهد و بعد نوشتن نمونه تست می شود و در انتها نمونه ها در کنار هم قرار گرفته و برنامه کلی را تشکیل می دهند و خطاهای برنامه بر طرف می شود.
این سبک در حالتی که دید خیلی عمیق نسبت به برنامه اصلی نداریم خوب است در مقابل برطرف کردن خطاهای برنامه مشکل و زمان گیر است.
یک جایگزین، توسعه طراحی شده است. به این صورت که دید سطح بالا به برنامه داشته باشیم. برای نمونه در مورد Time یک شیء است که ویژگی های آن ۳ عدد در مبنای ۶۰ است.
هنگامی ما توابع add_time و increment را می نویسیم. به طور موثر می توان در مبنای ۶۰ جمع را انجام دهیم.
یک پیشنهاد دیگر رهیافت به کل مساله است. به این صورت که زمان را تبدیل به عملیات صحیح کرده و عملیات مورد نیاز را روی آن انجام دهیم. مزیت این روش درک کامپیوتر از عملیات انجام شده بر روی اعداد صحیح است.
در ادامه مثالی از تبدیل زمان به عدد صحیح را داریم:
def time_to_int(time):\ minutes = time.hour * 60 + time.minute\ seconds = minutes * 60 + time.second\ return seconds\
همچنین تابعی که عدد صحیح را به زمان تبدیل کند:
def int_to_time(seconds):\ time = Time()
minutes, time.second = divmod(seconds, 60)\ time.hour, time.minute = divmod(minutes, 60)\ return time\
می توان این توابع را تست کرد که از نحوه عملکرد صحیح توابع مطمءن شد. یک راه برای تست کردن مقادیر مختلف به صورت زیر است:
time_to_int(int_to_time(x)) == x
بعد از تست می توان این تابع را در داخل توابع دیگر استفاده کرد به عنوان نمونه در تابع add_time به صورت زیر استفاده شده است:
def add_time(t1, t2):\ seconds = time_to_int(t1) + time_to_int(t2)\ return int_to_time(seconds)\
این ورژن از تابع کوتاه تر است و تصدیق آن راحت تر است. به عنوان تمرین تابع increment را با توابع time_to_int و int_to_time باز نویسی کنید.
اشکال زدایی
شیء Time در صورتیکه ثانیه و دقیقه بین ۰ و ۶۰ باشد تابع خوش تعریفی است. اما اگر ساعت عدد مثبتی باشد ساعت و دقیقه باید عدد کامل باشد و ثانیه می تواند عدد کسری باشد.
پیش نیاز هایی که باید همیشه درست باشد invariant نامیده می شود. اگر این پیش نیاز ها درست نباشد باعث می شود عملکرد تابع با مشکل روبرو می شود.
نوشتن توابعی که invariant ها را چک کند، به اشکال زدایی برنامه کمک می کند. برای مثال می توان تابع avoid_time را با هدف اینکه اگر invariant برقرار نباشد False بر می گرداند.
def valid_time(time):\ if time.hour < 0 or time.minute < 0 or time.second < 0:\
return False\ if time.minute >= 60 or time.second >= 60:\
return False\ return True\
می توان قبل استفاده از شیء از نوع Time آن را به صورت زیر چک کرد:
def add_time(t1, t2):\ if not valid_time(t1) or not valid_time(t2):\
raise ValueError('invalid Time object in add_time')\ seconds = time_to_int(t1) + time_to_int(t2)\ return int_to_time(seconds)\
یا می توان از assert statement استفاده کرد که invariant را چک کند و در صورتیکه درست نباشد خطا برگرداند:
def add_time(t1, t2):\ assert valid_time(t1) and valid_time(t2)\ seconds = time_to_int(t1) + time_to_int(t2)\ return int_to_time(seconds)\
assert از این جهت که بین کد با شرایط نرمال و کدی که برای چک کردن خطا است تفاوت قاءل می شود.
واژه نامه
prototype and patch: طرح توسعه برنامه که شامل نوشتن نمونه اولیه، تست و خطایابی و اصلاح آنها می باشد.
designed development: طرح توسعه برنامه که دید سطح بالا به برنامه دارد و نیاز بیشتری به برنامه ریزی دارد نسبت به توسعه افزایشی یا توسعه نمونه ها.
pure function: توابعی که تغییری بر روی ویژگی های آرگومان های ورودی تابع ایجاد نمی کنند.
modifier توابعی می توانند ویژگی های آرگومان های ورودی خود را تغییر دهند. خروجی این توابع void می باشد به این معنا که چیزی به عنوان خروجی بر نمی گردانند.
functional programming style: برنامه هایی که توابع استفاده شده در برنامه pure function هستند.
invariant: شرایطی که در طول زمان اجرای برنامه باید همیشه True باشند.
assert statement: بیانی که شرایط را چک میکند و در صورت برقرار نبودن exception رخ می دهد.
تمرین 16.7
کد مثال های این فصل در اینجا قابل دسترس است. http://thinkpython.com/code/Time1.py و راه حل های این مثال ها در اینجا موجودند: http://thinkpython.com/code/Time1_soln.py
تمرین 6
تابعی بنویسید به اسم mul_time که یک شی زمان و یک عدد میگیرد و یک شی زمان جدید را برمیگرداند که شامل محصول زمان اولیه و عدد ورودی است.
حالا از mul_time استفاده کنید و تابع دیگری بنویسید که یک شی زمان میگیرد که نشان دهنده زمان پایان یک مسابقه است و یک عدد که نمایش دهنده فاصله است و شی زمانی را برمیگرداند که نشان دهنده میانگین سرعت بر اساس زمان است (زمان بر مایل)
تمرین 7
ماژول datetime شی date و time را ارائه میکند که شبیه شی Date و Time معرفی شده در این فصل است ولی این اشیا متدها و عملگر های بهتری دارند. منابع را در این آدرس مطالعه کنید: http://docs.python.org/2/library/datetime.html
- از ماژول datetime برای نوشتن یک برنامه استفاده کنید که زمان فعلی را میگیرد و روز هفته را چاپ میکند.
- برنامه ای بنویسید که تاریخ تولد را میگیرد و سن کاربر و روز ها و ساعت ها و دقایق و ثانیه های طی شده از روز تولد را چاپ کند.
- برای دو نفر که در تاریخ های مختلف متولد شده اند یک روز وجود دارد که یکی از آنها دو برابر دیگری سن دارد. این روز روز دوبل آنهاست. برنامه ای بنویسید که دو تاریخ تولد را میگیرد و روز دوبل را محاسبه میکند.
- برای چالش بیشتر نسخه جامع تری بنویسید که روزی که یک نفر n برابر دیگری سن دارد را چاپ کند.