آموزش پایتون - فصل چهارم(عبارات شرطی و بازگشتی)

موضوع اصلی این فصل دستور if هست که بسته به شرایط داخل برنامه ، کدهای متفاوتی رو اجرا میکنه ولی قبلش میخوام که دوتا عملگر جدید رو بهتون معرفی کنم : عملگر تقسیم صحیح و عملگر باقیمانده .تقسیم صحیح و باقیمانده :تقسیم صحیح که با علامت // نشون داده میشه بعد از انجام عمل تقسیم ، نتیجه رو به نزدیک ترین عدد صحیح کوچکتر گرد میکنه. برای مثال فرض کنید که مدت پخش یک فیلمی برابر 105 دقیقه هست و میخوایم بدونیم که این زمان بر حسب ساعت چقدر میشه . در صورتی که از عملگر تقسیم مرسوم استفاده کنیم ، نتیجه بصورت یک عدد اعشاری به ما برگردونده خواهد شد :>>> minutes = 105 >>> minutes / 60 1.75این مدل خروجی زیاد به درد ما نخواهد خورد . درحالیکه عملگر تقسیم صحیح با نادیده گرفتن قسمت اعشاری ، تعداد ساعات رو به ما برمیگردونه :>>> minutes = 105 >>> hours = minutes // 60 >>> hours 1برای به دست آوردن باقیمانده هم میتونیم به شکل زیر عمل کنیم :>>> remainder = minutes - hours * 60 >>> remainder 45علاوه بر روش بالا ، برای به دست آوردن باقیمانده ی تقسیم میتونیم از عملگر ٪ استفاده کنیم که دو عدد رو بر هم تقسیم میکنه و باقیمانده رو به ما نمایش میده :>>> remainder = minutes % 60 >>> remainder 45عملگر باقیمانده (٪) بیشتر از چیزی که بنظر میرسه مفید و کاربردی هست ؛ برای مثال شما با استفاده از این عملگر میتونید بررسی بکنید که یک عدد بر عدد دیگه ای بخش پذیر هست یا نه (اگر باقیمانده تقسیم عدد x بر عدد y برابر صفر باشه یعنی x بر y بخش پذیره).نکته : اگر شما از پایتون نسخه ی ۲ استفاده میکنید ، عملگر تقسیم به شکل متفاوتی کار میکنه . به این صورت که اگر هردو عملوند تقسیم ، اعداد صیح باشند تقسیم بصورت تقسیم صحیح انجام خواهد شد ولی اگر هر یک از عملوندها از نوع اعشاری باشند ، تقسیم بصورت مرسوم انجام خواهد شد و نتیجه در قالب یک عدد اعشاری به ما برگشت داده خواهد شد.عبارات بولی (Boolean Expressions) :عبارت بولی به عبارتی گفته میشه که مقدار اون برابر با True و یا برابر با False باشه . در مثال های زیر از عملگر == استفاده میکنیم . این عملگر ، عملوندها رو با هم مقایسه میکنه و اگر مقدار اونها باهم برابر باشه ، True و در غیر اینصورت مقدار False رو به ما برمیگردونه :>>> 5 == 5 True >>> 5 == 6 Falseنکته : مقادیر True و False جزء مقادیر خاص هستند و به نوع داده ای bool تعلق دارند :>>> type(True) <class 'bool'>>>> type(False) <class 'bool'>عملگر == یکی از عملگرهای رابطه ای هست ، بقیه عملگرهای رابطه ای رو در قطعه کد زیر میتونید ببینید :x != y # x is not equal to y x > y # x is greater than y x < y # x is less than y x >= y # x is greater than or equal to y x <= y # x is less than or equal to yعملگرهای منطقی :سه نوع عملگر منطقی داریم : and ، or و not . معنی و مفهوم این عملگرها شبیه به معنی اونها در زبان انگلیسی هست . برای مثال جواب عبارت زیر فقط زمانی True خواهد بود که مقدار x بزرگتر از ۰ و کوچکتر از 10 باشد :x > 0 and x < 10و جواب عبارت زیر زمانی True خواهد بود که مقدار هردو یا حداقل یکی از شروط عبارت برابر با True باشد :n % 2 == 0 or n % 3 == 0و در نهایت میرسیم به عملگر not که مقدار یک عبارت بولی رو منفی میکنه ؛ برای مثال حاصل عبارت زیر تنها زمانی برابر True خواهد بود که مقدار عبارت (x > y) برابر False باشه :not (x > y) # result is True if x is less than or equal to yنکته : عبارت های بولی هم میتونن به عنوان عملوند در عملگرهای منطقی مورد استفاده قرار بگیرند. پایتون تمام اعداد غیر صفر رو به عنوان True تفسیر میکنه :>>> 42 and True Trueدستورات شرطی :برای نوشتن برنامه های مفید ، برنامه ی ما باید توانایی بررسی شرایط رو داشته باشه و بسته به اون شرایط ، رفتارش رو تغییر بده. دستورات شرطی این توانایی رو در اختیار ما قرار میدن ؛ ساده ترین نوعش هم دستور if هست :if x > 0 : print("x is positive")به عبارت بولیِ بعد از دستور if ، شرط گفته میشه . اگر مقدار شرط برابر با true باشه ، دستورات داخل if اجرا میشه وگرنه هیچ اتفاقی نمیوفته. نکته : محدودیتی در تعداد دستورات داخل بدنه ی if وجود نداره ولی حداقل باید یک خط کد نوشته بشه .در صورتیکه بخوایم ساختار دستور شرطیمون( if ) رو ایجاد بکنیم و دستورات داخل اون رو بعدا وارد کنیم ، از دستور pass استفاده میکنیم :if x < 0 : pass # TODO : need to handle negative values!جایگزین دستور شرطی (Alternative execution) :نوع دیگر دستور if ، دستور جایگزین شرط هست و زمانی به کار میره که دو حالت داشته باشیم و شرط برنامه مشخص میکنه که کدوم یکی باید اجرا بشه :if x % 2 == 0 : print(" x is even ") else : print(" x is odd ")همونطور که در مثال بالا میبینید ، اگر باقیمانده ی تقسیم x بر ۲ برابر با صفر باشه ، مقدار شرط برابر با true میشه و دستور داخل if اجرا میشه در غیر اینصورت دستور داخل بدنه ی دستور else اجرا خواهد شد(یعنی همون جایگزین دستور شرطی).دستورات شرطی زنجیری(Chained conditionals):بعضی موقع ها شرط برنامه بیشتر از دو حالت داره و برای اینکه بتونیم همه حالات شروط رو مورد بررسی قرار بدیم و نسبت به اون شرایط بتونیم تصمیم بگیریم از دستورات شرطی زنجیری استفاده میکنیم :if x < y : print(" x is less than y ") elif x > y : print(" x is greater than y ") else : print(" x and y are eqal ")دستور elif مخفف کلمه ی else if هست . در مثال بالا اگر مقدار x کوچکتر از مقدار y باشه ، دستورات داخل بدنه ی if اجرا خواهند شد وگرنه اگر مقدار x بزرگتر از مقدار y باشه ، دستورات داخل بدنه ی elif اجرا خواهند شد ؛ در غیر این صورت دستورات مربوط به else اجرا خواهند شد (و این کار در صورتی اتفاق خواهد افتاد که مقدار x و y برابر باشه).نکته : میدونیم که دستور elif بعد از دستور if قرار داده میشه تا حالت های دیگر شروط داخل برنامه رو مورد بررسی قرار بده و این رو هم میدونیم که اگر مقدار شرط دستور if برابر True باشه ، همه دستورات elif بعد از اون نادیده گرفته میشن حتی اگر مقدار شرط دستور elif هم برابر True باشه . با این مثال بهتر متوجه خواهید شد :x = 5 if 0 < x < 10 : print(" if statement accomplished") elif 2 < x < 8 : print(" elif statement accomplished") else : print("else statement accomplished")در مثال بالا مقدار متغیر x هم مابین ۰ و 10 هست و هم مابین 2 و 8 ؛ یعنی هم مقدار شرط دستور if برابر True هست و هم مقدار شرط دستور elif . ولی طبق نکته ای که بالا بهتون گفتم فقط دستورات داخل if اجرا خواهند شد و مهم نیست که مقدار شرط elif برابر True باشه یا False :Output : if statement accomplishedدستورات شرطی تودرتو (Nested Conditionals):ما میتونیم داخل بدنه ی هر دستور شرطی ، دستورات شرطی دیگه ای هم داشته باشیم ؛ به این حالت از دستورات ، دستورات شرطی تودرتو میگیم. برای مثال :if x == y : print(" x and y are equal") else: if x < y : print(" x is less than y ") else : print(" x is greater than y ")در مثال بالا اگر مقدار x برابر با مقدار y باشه ، دستورات داخل بدنه ی if اجرا خواهند شد وگرنه دستورات داخل بدنه ی else واقع در اولین لایه اجرا خواهند شد ؛ که خودش دو حالت داره : اگر مقدار x کوچکتر از مقدار y باشه ، دستورات داخل بدنه ی if واقع در دومین لایه اجرا خواهند شد در غیر این صورت (که میشه وقتی که مقدار x بزرگتر از مقدار y هست) ، دستورات داخل بدنه ی else واقع در دومین لایه اجرا خواهند شد.نکته : خوانایی دستورات شرطی تودرتو خیلی زود کم میشه ؛ برای همین تا جایی که میتونیم باید از نوشتن دستورات شرطی تو در تو خودداری کنیم.استفاده از عملگرهای منطقی میتونه دستورات شرطی تودرتوی مارو ساده تر کنه :if 0 < x : if x < 10 : print(" x is a positive single-digit number ")دستور print در مثال بالا فقط زمانی اجرا میشه که هردوتا شرط برقرار باشه . میتونیم کد بالا رو به اینصورت بازنویسی کنیم :if 0 < x and x < 10 : print(" x is a positive single-digit number")و همچنین میتونیم به این حالت هم بازنویسی کنیم :if 0 < x < 10 : print(" x is a single-digit number")بازگشت(Recursion):توابع نه تنها میتونن توابع دیگر رو فراخوانی بکنن ، بلکه میتونن خودشون رو هم فراخوانی کنن. برای مثال :def countDown(n): if n <= 0 : print(" BlastOff!") else : print(n) countDown(n - 1)اگر مقدار n برابر با 0 یا یک مقدار منفی باشه ، عبارت "BlastOff" نمایش داده خواهد شد در غیر اینصورت مقدار n نمایش داده میشه و تابع خودش رو فراخوانی میکنه و مقدار n - 1 رو به عنوان آرگومان به خودش پاس میده.چه اتفاقی میوفته اگر تابع رو بصورت زیر فراخوانی کنیم :>>> countDown(3)خب در مرحله ی اول ، روند اجرای تابع با مقدار n = 3 شروع میشه و چون مقدار n بزرگتر از 0 هست مقدار ۳ رو به ما نمایش میده و بعد خودش رو فراخوانی میکنه . در مرحله ی دوم ، روند اجرای تابع با مقدار n = 2 شروع میشه و چون مقدار n بزرگتر از 0 هست مقدار 2 رو به ما نشون میده و بعد خودش رو فراخوانی میکنه .در مرحله ی سوم ، روند اجرای تابع با مقدار n = 1 شروع میشه و چون همچنان مقدار n بزرگتر از 0 هست ، مقدار 1 رو به ما نشون میده و بعد خودش رو فراخوانی میکنه . در مرحله ی چهارم ، روند اجرای تابع با مقدار n = 0 شروع میشه ولی در این مرحله دیگه مقدار n بزرگتر از 0 نیست و به همین علت عبارت "!BlastOff" به ما نمایش داده میشه .نکته ی مهم : در مثال بالا شاید تصور کنید بعد از اینکه مقدار رشته ای "!BlastOff" به ما نشون داده شد ، روند اجرای برنامه از داخل تابع countDown به یکباره خارج میشه و اگر دستورات دیگه ای در ادامه ی تابع نوشته شده باشن ، اونهارو اجرا میکنه ولی اینطور نیست . وقتی تابعی بصورت بازگشتی خودش رو فراخوانی میکنه ، روند اجرای برنامه بصورت تودرتو داخل توابعی که فراخوانی شده اند پیشروی میکنه و یک ساختار تودرتو از اجراها و بازگشت ها ایجاد میکنه و روند اجرای برنامه به یکباره نمیتونه از این ساختار بیرون بیاد و به کارش ادامه بده . پس مجبوره مرحله به مرحله این ساختار تودرتو رو به سمت بیرون پیمایش کنه و روند اصلی اجرایی برنامه رو پیش بگیره.بعد از انجام شدنِ نکته ی بالا هست که خروجی زیر به کاربر نشون داده میشه:3 2 1 BlastOff!بازگشت نامتناهی(Infinite Recursion):اگر فرایند بازگشت (فراخوانی تابع توسط خودش) به شرط معین نرسه ، این فرایند تا بی نهایت ادامه پیدا میکنه و برنامه هیچوقت به پایان نمیرسه . به این حالت ، بازگشت نامتناهی گفته میشه. برای مثال :def recurse(): recurse()در اغلب محیط های برنامه نویسی ، برنامه ای که دارای بازگشت نامتناهی هست در عمل تا بی نهایت اجرا نمیشه . اگر فرایند بازگشت به ماکزیممِ حدِ تعیین شده برسه ، پایتون پیام خطای زیر رو به ما نشون خواهد داد : File "<stdin>" , line 2 , in recurse File "<stdin>" , line 2 , in recurse File "<stdin>" , line 2 , in recurse . . . File "<stdin>" , line 2 , in recurse RuntimeError : Maximum recursion depth exceedنکته : در صورتی که از توابع بازگشتی استفاده میکنیم همیشه باید در نظر داشته باشیم که فرایند بازگشت در نهایت به شرط تعیین شده ی ما برسه وگرنه تابع مورد نظر دچار بازگشت بی نهایت میشه.دریافت ورودی از کیبورد(Keyboard Input):برنامه هایی که تا الان نوشتیم ، هیچ ورودی ای رو از کاربر دریافت نمیکردن و همیشه یه کار مشابه رو انجام میدادن. پایتون تابعی درون-ساخت (Built-in) بنام input رو در اختیار ما گذاشته که وقتی اجرا میشه ، برنامه رو متوقف میکنه و منتظر میمونه تا کاربر چیزی رو از طریق کیبورد وارد بکنه و کلید enter رو بزنه تا روند اجرای برنامه ادامه پیدا کنه.نکته ی اول : تابع input ورودی کاربر رو بصورت string دریافت میکنه.نکته ی دوم : نام تابع input در پایتون نسخه ی ۲ ، raw_input هست.>>> text_1 = input() # in python 3.x what are you waiting for? >>> text_1 what are you waiting for? >>> text_2 = raw_input() # in python 2.x how are you? >>> text_2 how are you?قبل از گرفتن ورودی از کاربر بهتره پیامی رو بهش نمایش بدیم تا متوجه بشه که چه چیزی رو باید وارد کنه . تابع input میتونه پیام مورد نظر رو به عنوان آرگومان دریافت کنه :>>> name = input("what is your name?\n") what is your name? Milad sadeghi >>> name Milad Sadeghiنکته : درمثال بالا ، n\ یکی از کاراکتر های ویژه به معنی خط جدید هست ؛ به اینصورت که وقتی روند اجرای برنامه به این کاراکتر میرسه ، برنامه ، روند اجرا رو از یک خط پایین تر ادامه میده.اگر انتظار دارید که کاربر یک مقدار عددی صحیح رو وارد کنه ، باید مقدار برگشتی تابع input رو به int تبدیل کنید :>>> prompt = "How old are you?\n" >>> age = input(prompt) How old are you? 23 >>> int(age) 23اما اگر مقدار ورودی کاربر چیزی بغیر از رشته ی اعداد باشه ، با خطا مواجه خواهید شد : >>> age = input(prompt) How old are you? twenty three >>> int(age) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'twenty three'نحوه رسیدگی به این نوع خطاها رو بعدا بررسی خواهیم کرد.خطایابی (Debugging) :زمانی که یک خطای نحوی یا زمان اجرا رخ میده ، پیام خطا شامل اطلاعات زیادی هست که به نوبه ی خودش هم میتونه مفید باشه و هم گیج کننده . مفید ترین و به درد بخورترین بخش های یک پیام خطا عبارت اند از :نوع خطایی که رخ دادهجایی که خطا رخ دادهمثال برای خطای نحوی :>>> x = 5 >>> y = 6 File "<stdin>" , line 1 y = 6 ^ Indentati : unexpected indentخطاهای نحوی معمولا به راحتی قابل شناسایی و رفع هستند باستثنای فضاهای خالی موجود در کدهای برنامه که میتونن دردسرساز بشن . در مثال بالا ، خطا مربوط به خط دوم هست که توسط کلید فاصله (space) دچار تورفتگی شده . ولی همونطور که میبینید پیام خطا داره y رو نشون میده که میتونه گمراه کننده باشه. معمولا پیام خطا جایی که مشکل پیدا شده رو نشون میده در حالیکه ممکنه مشکل اصلی توی کدهای قبلی بوده باشه. این مساله برای خطاهای زمان اجرا هم صادق هست . فرض کنید میخوایم برنامه ای بنویسیم که طبق فرمول زیر نسبت سیگنال به نویز رو در واحد دسیبل برای ما حساب کنه :برای این کار ممکنه به شکل زیر عمل کنیم :import math signal_power = 9 noise_power = 10 ratio = signal_power // noise_power decibels = 10 * math.log10( ratio ) print(decibels)اگر برنامه ی بالا رو اجرا کنیم با خطای زیر مواجه خواهیم شد :Traceback (most recent call last): File "snr.py", line 5, in ? decibels = 10 * math.log10(ratio) ValueError: math domain errorپیام خطا خط پنجم رو نشون میده درحالیکه این خط هیچ مشکلی نداره . برای اینکه بفهمیم قضیه از چه قراره بهتره مقدار متغیر ratio رو چاپ کنیم (که در اینجا برابر با 0 هست). ایراد برنامه جایی هست که ما بجای استفاده از تقسیم اعشاری از تقسیم صحیح برای به دست آوردن مقدار متغیر ratio استفاده کردیم.نکته : شما باید پیام های خطا رو با دقت بخونید ولی این رو یادتون نره که همه ی چیز هایی که پیام های خطا میگن لزوما درست نیست (:تمرینها :ماژول time تابعی هم نام خودش (time)رو در اختیارمون گذاشته که زمان رو از مبدا تاریخ به وقت گرینویچ به ما نشون میده . مبدا تاریخ در سیستم های مبتنی بر یونیکس ، یکم ژانویه سال 1970 هست .>>> import time >>> time.time() 1563687834.2420669الف - برنامه ای بنویسید که زمان رو بخونه و بگه که این زمان مربوط به چه ساعت ، چه دقیقه و چه ثانیه ای از زمان امروز هست.ب - برنامه ای بنویسید که تعداد روزهای سپری شده از مبدا تاریخ تا به امروز رو محاسبه کنه.تابع is_triangle رو طوری بنویسید که سه آرگومان صحیح رو دریافت کنه و بسته به اینکه این آرگومان ها میتونن یک مثلث رو تشکیل بدن یا نه ، کلمه ی "yes" , "no" رو برا ما چاپ بکنه.برنامه ای بنویسید که طول اضلاع مثلث رو از کاربر بگیره و با استفاده از تابع is_triangle به ما نشون بده که آیا ورودی های کاربر میتونن یه مثلث رو تشکیل بدن یا نه.خروجی تابع زیر رو بررسی کنید :def recurse ( n, s ) : if n == 0 : print( s ) else : recurse ( n -1, n + s) recurse ( 3, 0)خروجی تابع بالا رو زمانی که بصورت زیر فراخوانی شده باشه بررسی کنید :recurse ( -1, 0 )خب ، اینم از فصل چهارم . امیدوارم مفید واقع بشهموفق باشید (: