Skip to content

6. استثناها

اکنون به استثناها نگاه خواهیم کرد.

  

برنامه (exceptions_01)

اسکریپت اول نیاز به رسیدگی به استثناها را نشان می‌دهد.


# -*- coding=utf-8 -*-

#یک خطا ایجاد می‌شود
x=4/0

ما عمداً یک خطا را ایجاد می‌کنیم تا اطلاعات تولیدشده توسط تفسیرگر را ببینیم. خروجی صفحه به شرح زیر است:

1
2
3
4
Traceback (most recent call last):
  File "D:\data\istia-1112\python\tutoriel\exceptions_01.py", line 4, in <module>
    x=4/0
ZeroDivisionError: integer division or modulo by zero

خط ۴ به ما می‌دهد:

  • نوع استثنا: ZeroDivisionError;
  • پیام خطای مرتبط: تقسیم عدد صحیح یا ماژولو بر صفر. این پیام به زبان انگلیسی است. این چیزی است که ممکن است بخواهیم آن را تغییر دهیم.

قاعدهٔ کلیدی در برنامه‌نویسی این است که باید تمام تلاش صورت گیرد تا از کرش‌های «غیرکنترل‌شده» مانند مورد بالا جلوگیری شود. حتی در صورت رخ دادن خطا، برنامه باید به‌درستی خاتمه یابد و اطلاعاتی دربارهٔ خطای رخ‌داده ارائه دهد.

سینتکس مدیریت استثنا به شرح زیر است:


try:
    actions susceptibles de lancer une exception
except [classe d'exception, ...]:
    actions de gestion de l'exception
finally:
   actions toujours exécutées qu'il y ait exception ou non

در داخل بلوک try**، به محض وقوع یک استثنا، اجرای برنامه متوقف می‌شود. در این حالت، اجرای برنامه با اقدامات موجود در بند except** ادامه می‌یابد.

The


except [MyException, ...]:

استثناهای از نوع MyException یا انواع مشتق‌شده را رهگیری می‌کند. هنگامی که یک استثنا در داخل یک بلوک try رخ می‌دهد، تفسیرگر بندهای except مرتبط با بلوک try را به ترتیبی که نوشته شده‌اند بررسی می‌کند. این فرآیند در اولین بند except که قادر به رسیدگی به استثنای رخ‌داده است، متوقف می‌شود. اگر هیچ‌کدام را پیدا نکند، استثنا به متد فراخوانی‌کننده بازگردانده می‌شود. اگر آن متد یک بلوک try**/except** داشته باشد، استثنا در آنجا مدیریت می‌شود؛ در غیر این صورت، به زنجیره متدهای فراخوانی‌شده بالاتر منتقل می‌شود. در نهایت، به تفسیرگر پایتون می‌رسد که برنامه در حال اجرا را متوقف کرده و پیغام خطایی از نوع نشان داده شده در مثال قبلی را نمایش می‌دهد. بنابراین، قاعده این است که برنامه اصلی باید تمام استثناهایی را که ممکن است از متدهای فراخوانی‌شده منتقل شوند، بگیرد.

یک استثنا اطلاعاتی در مورد خطایی که رخ داده است، حمل می‌کند. این اطلاعات را می‌توان با استفاده از سینتکس زیر بازیابی کرد:


except MyException as informations:

information یک تپل (tuple) است که شامل اطلاعات مربوط به استثنا می‌باشد.

سینتکس


except MyException, erreur:

پیام خطای مرتبط با استثنا را به متغیر erreur تخصیص می‌دهد.

برای پرتاب یک استثنا، از سینتکس زیر استفاده می‌شود


raise MyException(param1, param2, ...)

که در اکثر موارد، MyException کلاسی است که از کلاس Exception ارث‌بری شده است. پارامترهای ارسال‌شده به سازنده کلاس در بند except در ساختارهای مدیریت استثنا در دسترس خواهند بود.

این مفاهیم با اسکریپت زیر نشان داده شده‌اند.


برنامه (exceptions_02)

اسکریپت زیر به‌طور صریح خطاها را مدیریت می‌کند:


# -*- coding=utf-8 -*-

i=0
# یک خطا ایجاد و مدیریت می‌شود
x=2
try:
    x=4/0
except ZeroDivisionError, erreur:
    print ("%s : %s ") % (i, erreur)
# مقدار x تغییر نکرده است
print "x=%s" % (x)

# ما دوباره شروع می‌کنیم
i+=1
try:
    x=4/0
except Exception, erreur:
    # ما عمومی‌ترین استثنا را می‌گیریم
    print ("%s : %s ") % (i, erreur)

# ما می‌توانیم چندین نوع استثنا را دریافت کنیم 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # این استثنا در اینجا رخ نمی‌دهد
    print ("%s : %s ") % (i, erreur)
except Exception, erreur:
    # ما عمومی‌ترین استثنا را می‌گیریم
    print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #یک نوع خاص گرفته می‌شود
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)

#بیایید با تغییر ترتیب دوباره امتحان کنیم 
i+=1
try:
    x=4/0
except ValueError, erreur:
    # این استثنا در اینجا رخ نمی‌دهد
    print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
    #یک نوع خاص گرفته می‌شود
    print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
    # عمومی‌ترین استثنا گرفته می‌شود
    print ("%s : (Exception) %s ") % (i, erreur)

#یک عبارت `except` بدون هیچ آرگومانی
i+=1
try:
    x=4/0
except:
    # ما به ماهیت استثناء یا پیام خطا کاری نداریم
    print ("%s : il y a eu un probleme ") % (i)

# نوع دیگری از استثنا
i+=1
try:
    x=int("x")
except ValueError, erreur:
    print ("%s : %s ") % (i, erreur)

#یک استثنا اطلاعاتی را در یک تپل در دسترس برنامه حمل می‌کند
i+=1
try:
    x=int("x")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# استثناها می‌توانند پرتاب شوند
i+=1
try:
    raise ValueError("param1","param2","param3")
except ValueError as infos:
    print ("%s : %s ") % (i, infos)

# می‌توانید استثناءهای خود را ایجاد کنید
class MyError(Exception):
    pass

#استثنا MyError پرتاب می‌شود
i+=1
try:
    raise MyError("info1","info2", "info3")
except MyError as infos:
    print ("%s : %s ") % (i, infos)

#استثناء MyError پرتاب می‌شود
i+=1
try:
    raise MyError("mon msg d'erreur")
except MyError, erreur:
    print ("%s : %s ") % (i, erreur)

# می‌توانید هر نوع شیء را پرتاب کنید
class Objet:
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

i+=1
try:
    raise Objet("pb...")
except Objet as erreur:
    print "%s : %s" % (i, erreur)

# بند finally همیشه اجرا می‌شود
# صرف‌نظر از اینکه یک استثنا رخ دهد یا خیر
i+=1
x=None
try:
    x=1
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

i+=1
x=None
try:
    x=2/0
except:
    print "%s : exception" % (i)
finally:
    print "%s : finally x=%s" % (i,x)

یادداشت‌ها:

  • خطوط ۶–۹: تقسیم بر صفر مدیریت می‌شود؛
  • خط ۸: استثنای دقیقی که رخ می‌دهد گرفته می‌شود؛
  • خط ۱۱: به دلیل استثناء رخ‌داده، به x مقداری تخصیص داده نشده و در نتیجه تغییر نکرده است؛
  • خطوط ۱۵–۱۹: ما همان فرآیند را تکرار می‌کنیم اما یک استثنای سطح بالاتر از نوع Exception را می‌گیریم. از آنجایی که استثنای ZeroDivisionError از کلاس Exception مشتق شده است، بند except آن را خواهد گرفت؛
  • خطوط ۲۳–۳۳: چندین عبارت except برای رسیدگی به انواع استثناءهای متعدد گنجانده شده است. تنها یک عبارت except اجرا خواهد شد، یا اگر استثناء با هیچ عبارت «except» مطابقت نداشته باشد، هیچ‌کدام اجرا نخواهند شد؛
  • خطوط ۵۱–۵۵: بند except ممکن است هیچ آرگومانتی نداشته باشد. در این صورت، تمام استثناها را متوقف می‌کند؛
  • خطوط ۵۹–۶۲: استثنای ValueError را معرفی می‌کنند؛
  • خطوط ۶۶–۶۹: اطلاعاتی که توسط استثنا حمل می‌شود بازیابی می‌شود؛
  • خطوط ۷۳–۷۶: نحوه پرتاب یک استثنا را معرفی می‌کند؛
  • خطوط ۷۹–۸۴: کاربرد یک کلاس استثنای سفارشی، MyError، را نشان می‌دهد؛
  • خطوط ۷۹–۸۰: کلاس MyError به سادگی از کلاس پایه Exception ارث می‌برد. این کلاس هیچ چیزی به کلاس پایه خود اضافه نمی‌کند. با این حال، اکنون می‌توان آن را به صراحت در عبارت‌های except نام برد؛
  • خطوط ۹۷–۱۰۷: نشان می‌دهد که در پایتون، در واقع می‌توانید هر نوع ابجکت را پرتاب کنید، نه فقط ابجکت‌هایی که از کلاس Exception مشتق شده‌اند؛
  • خطوط ۱۰۹–۱۲۷: کاربرد عبارت finally را نشان می‌دهند.

خروجی صفحه به شرح زیر است:

0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None

برنامه (exceptions_03)

این اسکریپت جدید نشان می‌دهد که استثناها چگونه در زنجیره متدهای فراخوانی به سمت بالا منتقل می‌شوند:


# -*- coding=utf-8 -*-

#یک استثنای سفارشی
class MyError(Exception):
    pass

# سه متد
def f1(x):
    # استثناها مدیریت نمی‌شوند – آنها به‌طور خودکار منتقل می‌شوند
    return f2(x)

def f2(y):
    # استثناها مدیریت نمی‌شوند – به‌طور خودکار منتقل می‌شوند
    return f3(y)

def f3(z):
    if (z % 2) == 0:
        # اگر z زوج باشد، یک استثنا پرتاب می‌شود 
        raise MyError("exception dans f3")
    else:
        return 2*z

#---------- main

# استثناها در زنجیره متدهای فراخوانی‌شده به سمت بالا منتقل می‌شوند
#تا زمانی که یک متد آن را دریافت کند. در این صورت، این متد `main` خواهد بود
try:
    print f1(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

#یک متد می‌تواند استثناءهایی را که منتقل می‌کند غنی‌سازی کند
def f4(x):
    try:
        return f5(x)
    except MyError as infos:
        # استثناء غنی‌سازی شده و سپس دوباره پرتاب می‌شود
        raise MyError(infos,"exception dans f4")

def f5(y):
    try:
        return f6(y)
    except MyError as infos:
        # استثناء را غنی‌سازی کرده و سپس دوباره آن را پرتاب می‌کنیم
        raise MyError(infos, "exception dans f5")

def f6(z):
    if (z % 2) == 0:
        #یک استثنا پرتاب می‌شود
        raise MyError("exception dans f6")
    else:
        return 2*z

#---------- main
try:
    print f4(4)
except MyError as infos:
    print "type :%s, arguments : %s" % (type(infos),infos)

یادداشت‌ها:

  • در خطوط ۲۷–۳۰، در فراخوانی main → f1 → f2 → f3 (خط ۲۸)، استثنای MyError که توسط f3 پرتاب شده است، تا main بالا خواهد رفت. سپس توسط بند except در خط ۲۹ رسیدگی خواهد شد؛
  • خطوط ۵۵–۵۸: در فراخوانی main → f4 → f5 → f6 (خط ۵۶)، استثنای MyError که توسط f6 پرتاب می‌شود، تا main بالا خواهد رفت. سپس توسط بند except در خط ۲۹ رسیدگی خواهد شد. این بار، هنگامی که در زنجیره متدهای فراخوانی صعود می‌کند، استثنای MyError با اطلاعاتی که توسط هر متد در طول مسیر اضافه شده، غنی می‌شود.

خروجی صفحه به شرح زیر است:

type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')