هنر برنامه نویسی با پایتون ، ‌‌‌‌‌مدیریت خطا

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

در فرهنگ واژگان زبان های برنامه نویسی ، به خطا یا مشکلی که به هنگام اجرای برنامه رخ می دهدوجریان عادی برنامه را از مسیر طبیعی خود خارج می سازد و موجب توقف اجرای برنامه و پایان یافتن غیر عادی آن می شود یک استثنا[1] گفته می شود. یک استثنا اگر به درستی رسیدگی نشود می تواند اجرای برنامه را با شکست روبرو سازد. از این رو در زبان های برنامه نویسی مدیریت استثنا بخش بسیار مهمی از زبان است. مدیریت استثنا سازوکاری برای برای به دام انداختن  و رسیدگی به خطای رخ داده در طول اجرای برنامه است.این سازو کار در زبان برنامه نویسی پایتون با استفاده از ساختاری به نام try انجام می شود. الگوی کلی نگارش و بکارگیری این ساختار به شکل زیر است :

 

 

try:

       # try Block

except:

       # except Block

else:

       # else Block

finally:

      # Finally Block

 هر بخش جداگانه از این ساختار دارای نقشی متفاوت در فرآیند به دام انداختن و رسیدگی به خطاست :  

بخش try   محل به دام انداختن خطای رخ داده است. پس قطعه[2] کدی از برنامه که ممکن است موجب رخ دادن یک استثنا شود. در بدنه رهنمون try نوشته می شود. وجود بخش try در ساختار مدیریت خطا اجباری است.

 بخش except مسئول رسیدگی به استثنای رخ داده است به بیانی ساده تر  قطعه کدی که خطای رخ داده در بخش try را مدیریت می کند در بدنه رهنمون except نوشته می شود ،کد نوشته شده در بدنه این رهنمون تنها در صورتی اجرا می شود که به هنگام اجرای برنامه ، در کد نوشته شده در بدنه رهنمون try خطایی به دام افتاده باشد. وجود بخش except در مدیریت خطا اجباری است. ساختار مدیریت خطا می تواند دارای بیش از یک بخش except باشد.

اگر می خواهید در صورت رخ ندادن خطا در بخش try کد خاصی اجرا شود آن را در بدنه رهنمون else بنویسید. قطعه کد نوشته شده در بخش else تنها زمانی اجرا می شود که در قطعه کد نوشته شده در بخش try استثنایی رخ نداده باشد. وجود بخش else در مدیریت خطا اجباری نیست. و می توان از نوشتن آن چشم پوشی کرد.

 بخش finally مسئول اجرای کدی است که باید همواره اجرا شود یعنی قطعه کد نوشته شده در بدنه رهنمون finally  بدون توجه به اینکه در کد نوشته شده در بدنه try  خطایی رخ داده است یا نه ، اجرا خواهد شد. وجود بخش finally در مدیریت خطا اجباری نیست و اگر به آن نیاز نباشد می توان از نوشتن آن چشم پوشی کرد. بسیاری از برنامه نویسان از این بخش برای آزاد سازی منابع مهمی چون اتصالات پایگاه داده ، بستن ارتباطات شبکه و بستن فایلهایی که درطول اجرای برنامه با استفاده از رهنمون open باز می شوند استفاده می کنند.

مثال : در برنامه زیر با رسیدن اجرای برنامه به عبارت n = divided /divisor  خطای تقسیم بر صفر رخ می دهد. بنابراین اجرای برنامه متوقف شده و مفسر پایتون خطای  ZeroDivisionError: division by zero   را اعلام خواهد کرد.

divided = 45

divisor = 0

n = divided /divisor  

print(n)

divisor = int(input("Please Enter Number: "))

n = divided /divisor

print(n)

مثال : بازنویسی برنامه مثال قبل با استفاده از ساختار مدیریت استثنا ، در کد بازنویسی شده بارخ دادن خطا در قطعه Try برنامه در هم نمی شکند و متوقف نمی شود بلکه با نمایش پیام مناسب به کاربر اجرای برنامه را از نخستین خط  پس از قطعه except از سر می گیرد.

divided = 45

divisor = 0

try:

            n = divided /divisor

            print(n)

except:

            print("Error : Divided by Zero")

divisor = int(input("Please Enter Number: "))

n = divided /divisor

print(n)

به دام انداختن نوع خاصی از استثنا

رویکرد درست در پاسخ به استثناهای رخ داده در برنامه این است که نوع استثنایی که می خواهیم در بخش try به دام انداخته شود را مشخص کنیم با این کار می توان به هر استثنا بر اساس نوع و ماهیت آن پاسخ مناسبی داد و از رسیدگی به تمامی خطاهای رخ داده با روشی یکسان دوری گزید. به این منظور می توانید نوع استثنای مورد نظر خود را در بخش except معرفی کنید. در ساختار مدیریت خطا امکان استفاده از چند بخش except متفاوت که هر یک استثنای خاصی را رسیدگی می کند نیز وجود دارد.

مثال :  در کد زیر فقط استثنای ZeroDivisionError   به دام می افتد این استثنا زمانی رخ می دهد که عملوند سمت راست عملگر تقسیم عدد صفر باشد. یعنی تقسیم بر صفر رخ دهد.

divided = 45

divisor = 0

try:

   n = divided /divisor

   print(n)

except ZeroDivisionError as exp:

   print(exp)

مثال : در کد زیر هم استثنای ZeroDivisionError  و هم استثنای NameError به دام می افتند. استثنای NameError زمانی رخ می دهد که مفسر پایتون در دامنه محلی و یا سراسری نتواند یک نام بکاررفته در کد را بیابد. در مثال زیر متغیر x در دستور print(x) استفاده شده در حالی که در هیچ دامنه ای از کد تعریف نشده است.

divided = 45

divisor = 15

try:

    n = divided /divisor

    print(n)

    print(x)

except ZeroDivisionError as exp:

    print(exp)

except NameError as exp2:

    print(exp2

مثال : استثنای ValueError   زمانی رخ می دهد که یک مقدار نادرست به یک عبارت یا یک تابع ارسال گردد برای مثال ورودی تابع یا متغیر به کار رفته در عبارت از نوع عددی است اما مقداری از نوع رشته ای به آن داده می شود . برای نمونه در کد زیر تابع int() مقدار  گرفته شده از کاربر توسط دستور input() را به یک عدد صحیح تبدیل می کند اما چنانچه مقدار گرفته شده از کاربر غیر قابل تبدیل شدن به یک عدد صحیح باشد (برای مثال کاربر مقدار "aa" را وارد کند)  استثنای ValueError   رخ خواهد داد.

while True:

    try:

        x = int(input("Pleas Enter Numbers:"))

        break

    except ValueError:

        print("That was not valid number,Try again")

می توانید چندین نوع استثنا را در قالب یک tuple و تنها با یک رهنمون except رسیدگی کنید:

مثال :

divided = 45

divisor = 15

try:

    n = divided /divisor

    print(n)

    print(x)

except (ZeroDivisionError,NameError, TypeError) as exp:

    print(exp)

 

به هنگام استفاده از ساختار برنامه نویسی try به دام انداختن و رسیدگی به خطا به شکل زیر انجام می شود :

1-    نخست قطعه کد نوشته شده در بدنه try اجرا می شود.

2-     اگر استثنایی در بخش try رخ ندهد ، بخش except نادیده گرفته می شود و بخش else یا finally در صورت وجود اجرا خواهند شد ، فرآیند مدیریت خطا پایان می یابد و اجرای برنامه از نخستین خط کد پس از ساختار try ادامه خواهد یافت.

3-    اگراستثنایی در بخش try به دام انداخته شود یعنی در قطعه کد موجود در بدنه try استثنایی رخ دهد اجرای کد در همان نقطه ای که خطا رخ داده است پایان می یابد و از اجرای باقی مانده کد چشم پوشی می شود. حال چنانچه نوع استثنای رخ داده با نوع استثنای تعریف شده در هریک ازexcept ها مطابقت داشته باشد قطعه کد موجود در بدنه except وابسته اجرا می شود و پس از آن بخش finally درصورت وجود اجرا خواهد شد و سپس اجرای برنامه از نخستین خط کد پس از ساختار try ادامه می یابد.

4-    اگر نوع استثنای رخ داده با هیچ یک از استثناهای تعریف شده در  except ها مطابقت نداشت. مفسر پایتون استثنای رخ داده را یک استثنای مدیریت نشده[3] شناسایی می کند و  با نمایش یک پیغام خطا  به اجرای برنامه پایان می دهد.

 

 

مثال : در کد زیر نوع استثنایی که رخ می دهد تقسیم بر صفر یا همان  ZeroDivisionError  است و چون نوع استثنای رخ داده برابر با هیچ یک از انواع استثنای تعریف شده در  except ها نیست مفسر پایتون خطای رخ داده را به عنوان یک استثنای مدیریت نشده شناسایی می کند و با نمایش پیغام خطای division by zero به اجرای برنامه پایان می دهد.

divided = 45

divisor = 0

try:

    n = divided /divisor

    print(n)

except NameError as exp2:

      print(exp2)

except OverflowError as exp3:

      print(exp3)

print("hello World")

دستور raise

برای وادارکردن مفسر پایتون به اعلان یک استثنا می توانیداز رهنمونraise استفاده کنید برای مثال فرض کنیدتابعی برای تبدیل گاهشمار جلالی (هجری خورشیدی) به گاهشمارمیلادی نوشته اید ، این تابع دارای سه ورودی به نامهای JalaliYear  ، JalaliMonth و JalaliDay است که به ترتیب سال ، ماه و روز تاریخ جلالی را در قالب سه عدد صحیح به عنوان ورودی می پذیرد و چون در تقویم جلالی هر سال به 12 ماه تقسیم می شود بنابراین اگر آرگومان ارسالی به تابع برای پارامتر JalaliMonth عددی کوچکتر از 1 یا بزرگتر از 12 باشد لازم است با تولید یک استثنای مناسب کد فراخواننده تابع را از این موضوع آگاه کنید تا بتواند خطای رخ داده را به درستی مدیریت کند.

مثال :

def jalali2Milady(JalaliYear, JalaliMonth , JalaliDay ):

    if JalaliMonth < 1 or JalaliMonth >12:

         raise Exception("Error Wrong Month value")

    print("{}\{}\{}".format(JalaliYear , JalaliMonth , JalaliDay) )

try:

    jalali2Milady(1402, 4, 21)

    jalali2Milady(1402, 14, 21)

except Exception as exp:

    print(exp)

 

مثال :

def self_sum_int(number):

    if not isinstance(number, int):

        raise TypeError("Wrong Type of input number")

    return number + number

try:

    print(self_sum_int(55))

    print(self_sum_int('aa'))

except Exception as exp:

    print(exp)

ایجاد یک استثنای سفارشی

در زبان برنامه نویسی پایتون با ساخت کلاسی که از کلاس Exception یا یکی از زیرکلاسهای آن ارث می برد می توان یک Exception  سفارشی ساخت.

مثال :

class JalaliCalenderException(Exception):

    def __init__(self, number, message):

        self.number = number

        self.message = message

        super().__init__(self.message)

    def __str__(self):

         return f'JalaliCalenderException:{self.number} {self.message}'

def jalali2Milady(JalaliYear, JalaliMonth , JalaliDay ):

    if JalaliMonth < 1 or JalaliMonth >12:

           raise JalaliCalenderException(JalaliMonth, "Error:Wrong Month value")

    return "{}\{}\{}".format(JalaliYear , JalaliMonth , JalaliDay)

try:

      print(jalali2Milady(1402,14,21))

except JalaliCalenderException as je:

    


[1] Exception

[2] Block

[3] unhandled exception

نظرات 0 + ارسال نظر
برای نمایش آواتار خود در این وبلاگ در سایت Gravatar.com ثبت نام کنید. (راهنما)
ایمیل شما بعد از ثبت نمایش داده نخواهد شد